栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

0.96寸OLED(四)多级菜单实现

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

0.96寸OLED(四)多级菜单实现

文章目录
  • 前言
  • 一、结构体定义
  • 二、相关函数
    • 1.页面刷新
    • 2.页面清除
    • 3.菜单更新
    • 4.GUI按键检测
    • 5.初始化
  • 二、函数注意
    • 1.仅运行一次的函数
    • 2.菜单切换的函数
    • 3.重复循环的函数
    • 4.主函数循环和调用方法


前言

本次是基于0.96寸OLED的多级菜单设计,找了一下网上的菜单写法,发现很多是那种利用一个数组记录界面的,我觉得这样不是很直观。我希望写成那种可以不断延伸拓展的界面,界面数量,菜单级数可以任意指定的类型。

就像XMIND思维导图工具一样,因此我写了一个简单的多级菜单管理界面。

下面直接上代码

一、结构体定义

首先创造一些结构体存放界面所需的变量

#ifndef __GUI_096_H
#define __GUI_096_H
#include "main.h"
#include "oled.h"
#define Menu_level 5 //菜单级数
#define Menu_Length 20 //单个菜单长度
typedef struct oled_fun
{
    uint8_t string[20]; //菜单名
    void (*func) (void); //菜单函数

}GUI_Menu;
typedef enum GUI_Button
{
      Gui_Up,
      Gui_Down,
      Gui_Right,
      Gui_Left,
      Gui_No,
}GUI_Button;
typedef struct Oled_Gui
{
    uint8_t Pause;   //Gui暂停标志位
    uint8_t Menu_Len; //Gui菜单长度
    GUI_Button Button; //Gui按键
    uint8_t Begin_Index;  //Gui菜单起始
    uint8_t Now_Inedx;  //Gui菜单选中
    GUI_Menu Menu[Menu_Length];  //Gui菜单
    void (*Last_Func[Menu_level]) (void);    //Gui上级函数记录
    void (*Now_Func) (void);   //Gui选中函数
}Gui;
extern Gui My_Gui;
void gui_init();
void Menu_Refresh(void);
void Menu_Clear();
void Menu_Update(uint8_t len,GUI_Menu* the_next);
#endif///
二、相关函数 1.页面刷新
void Menu_Refresh()
{
   OLED_Clear(); 
   for(int i=My_Gui.Begin_Index;i
        if(i==(My_Gui.Now_Inedx))
        OLED_ShowString(0,16*(i-My_Gui.Begin_Index),My_Gui.Menu[i].string,16,0); 
        else
        OLED_ShowString(0,16*(i-My_Gui.Begin_Index),My_Gui.Menu[i].string,16,1); 
        if(i-My_Gui.Begin_Index==3)
          break;
   }
  OLED_Refresh();
}
2.页面清除
void Menu_Clear()
{
  memset( (uint32_t *)(&(My_Gui.Menu[0])), 0, My_Gui.Menu_Len*sizeof(My_Gui.Menu[0]));
  My_Gui.Menu_Len=0;
  My_Gui.Begin_Index=0; 
  My_Gui.Now_Inedx=0; 
  OLED_Clear(); 
  OLED_Refresh();
}
3.菜单更新
void Menu_Update(uint8_t len,GUI_Menu* the_next)
{
    memset( (uint32_t *)(&(My_Gui.Menu[0])), 0, My_Gui.Menu_Len*sizeof(My_Gui.Menu[0]));
    My_Gui.Begin_Index=0; 
    My_Gui.Now_Inedx=0; 
    My_Gui.Menu_Len=len;
    for(int i=0;i
           My_Gui.Menu[i]=the_next[i];  
    }     
    Menu_Refresh();     
}
4.GUI按键检测
void gui_init_tick()
{
  //GUI没有暂停的情况下处理按键
  if(My_Gui.Pause==0)
  {
      if(My_Gui.Button==Gui_Up)
      {
          My_Gui.Button=Gui_No;
          if(My_Gui.Now_Inedx>0)
          {
            //向上移位选项然后刷新屏幕
             My_Gui.Now_Inedx--;
             if(My_Gui.Now_Inedx
          My_Gui.Button=Gui_No;
          //向下移位选项然后刷新屏幕
          if(My_Gui.Now_Inedx<(My_Gui.Menu_Len-1))
          {
            My_Gui.Now_Inedx++;
            if(My_Gui.Now_Inedx-My_Gui.Begin_Index>3)
              My_Gui.Begin_Index=My_Gui.Now_Inedx-3;
            Menu_Refresh();
          }   
      }
      else if(My_Gui.Button==Gui_Right)
      {
          My_Gui.Button=Gui_No;
          //选择函数
          if(My_Gui.Menu[My_Gui.Now_Inedx].func!=NULL)
          {
            //现函数进入上级存储
            for(int i=0;i
              if(My_Gui.Last_Func[i]==NULL)
              {
                  My_Gui.Last_Func[i]=My_Gui.Now_Func;
                  break;
              }
            }
            //修改现函数
            My_Gui.Now_Func=My_Gui.Menu[My_Gui.Now_Inedx].func;
            My_Gui.Now_Inedx=0;
            My_Gui.Begin_Index=0;
            My_Gui.Pause=1;            //暂停GUI
          }
    }
    else if(My_Gui.Button==Gui_Left)
    {
          My_Gui.Button=Gui_No;   
          //取出存储的函数
          for(int i=Menu_level-1;i>=0;i--)
          {
            if(My_Gui.Last_Func[i]!=NULL)
            {
                My_Gui.Now_Func=My_Gui.Last_Func[i];
                My_Gui.Last_Func[i]=NULL;
                break;
            }
          }
          My_Gui.Now_Inedx=0;
          My_Gui.Begin_Index=0;
          My_Gui.Pause=1;
    }
  }
  else
    My_Gui.Button=Gui_No;
}
5.初始化
GUI_Menu Menu1[]={{"A",.func=func_A},
                {"AA",.func=func_B},
                {"AAA",.func=func_C},
                {"AAAA",.func=func_D},
                {"AAAAA",.func=func_D},
                {"AAAAAA",.func=func_D},
                {"AAAAAAA",.func=func_D},
                {"AAAAAAAA",.func=func_D},
};
void gui_init()
{
    for(int i=0;i 
二、函数注意 

本次是基于0.96寸OLED的多级菜单设计,可以看到我定义的结构体内部函数指针是返回空类型且没有输入变量。并且菜单调用的函数基本上可以分为三类,一类是仅仅运行一次,比如修改了什么参数的函数,一类是菜单更新,因为下一级可能不是函数而是一个菜单更新的函数,最后一类是那种while循环的类型,这种函数需要一个while()检测让函数停止,不然函数会一直运行跳转不出来,这个函数需要我们自己加上停止条件。

1.仅运行一次的函数

这类函数往往很快运行完之后直接跳出来。比如一个LED的翻转函数,前面需要先清除一下当前菜单。

void func_A(void)
{
  Menu_Clear();
  HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);  
}
2.菜单切换的函数

这类函数往往是更新界面函数,向下一级函数跳转,可以直接参考我下面的写法进行调用。

GUI_Menu Menu2[]={{"B",.func=func_A},
                {"中景园",.func=func_B},
};
void func_C(void)
{  
  Menu_Update(2,Menu2);
  HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
3.重复循环的函数

这类函数需要用户自己添加结束条件

void func_B(void)
{
   Menu_Clear();
   while(flag==1)
   {
    //用户自己添加内容   
   
   }
}
4.主函数循环和调用方法

开一个定时器内部进行不断调用

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
  if(htim->Instance==TIM4)
  {
   //HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
   //HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);
  }
  else  if(htim->Instance==TIM3)
  {
    gui_init_tick();
  }
}

主函数的写法就很简单了

#include "sys.h"
int main(void)
{
  hard_steup();
  while(1)
  {
    if(My_Gui.Pause==1)
    {
      My_Gui.Now_Func();
      My_Gui.Pause=0;
    }
  }
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/990752.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号