+1 голос

Всем доброго времени суток. При создании моего проекта появилась необходимость в реализации многоуровневого меню. В сети удалось найти, как я считаю, достаточно простой образец одноуровневого меню на 6 кнопок. Все кнопки я подвязал на отдельные цифровые пины, благо МЕГА это позволяет. Только мучаюсь уже много дней над тем как же реализовать второй уровень, а конкретно - перемещение между строками вверх и вниз, я их назвал m==30, m==31, m==32. Это должен быть 2 уровень для строки m==3. Enter и Esc вроде разобрался. Это мой первый проект, делаю его модульно, по мере продвижения объединяю. Очень надеюсь на понимание и помощь чтобы использовать это для дальнейших проектов. Возможно эта наработка пригодится ещё кому нибудь. Как одноуровневое меню это идеальное решение. Скетч меню прилагаю ниже.

 

#include <LiquidCrystal_I2C.h>            // подключаем библиотеку дисплея
#include <Wire.h>                       // подключаєм библиотеку
LiquidCrystal_I2C lcd (0x3F,16,2);        // указиваем тип дисплея (конвектора)

int m=0; // переменная для экранов меню
int p1=0; // переменная  1
int p2=0; // переменная  2
int p3=0; // переменная  3

#define nextPin 46 // кнопка меню на 6 входе ENTER
#define prevPin 42 // кнопка возврата ESC
#define upPin 48 // увеличение значения отображаемого параметра +
#define downPin 44 // уменьшение значения -
#define enterPin 52
#define escPin 50

boolean buttonnextWasUp = true;
boolean buttonprevWasUp = true;
boolean buttonupWasUp = true;
boolean buttondownWasUp = true;
boolean buttonenterWasUp = true;
boolean buttonescWasUp = true;

boolean ledEnabled = false;

void setup()
{

// Установка пинов как входов

pinMode(nextPin, INPUT_PULLUP);
pinMode(prevPin, INPUT_PULLUP);
pinMode(upPin, INPUT_PULLUP);
pinMode(downPin, INPUT_PULLUP);
pinMode(enterPin, INPUT_PULLUP);
pinMode(escPin, INPUT_PULLUP);

Serial.begin(9600);
lcd.init();            // инициализация LCD
lcd.backlight();       // включаем подсветку

// Вывод приветствия
lcd.print( "MY PROGECT" );
// задержка
delay (1000);
}

void loop ()
{
  boolean buttonnextIsUp = digitalRead(nextPin);
  boolean buttonprevIsUp = digitalRead(prevPin);
  boolean buttonupIsUp = digitalRead(upPin);
  boolean buttondownIsUp = digitalRead(downPin);
  boolean buttonenterIsUp = digitalRead(enterPin);
  boolean buttonescIsUp = digitalRead(escPin);

//обработка нажатия кнопки enter
if (buttonenterWasUp && !buttonenterIsUp)
{
delay(10);
buttonenterIsUp = digitalRead(enterPin);
if (!buttonenterIsUp)
{
if (m==3)
{
m=30;
}
delay(100);
lcd.clear();
}
}

// обработка нажатия кнопки esc
if (buttonescWasUp && !buttonescIsUp)
{
delay(10);
buttonescIsUp = digitalRead(escPin);
if (!buttonescIsUp)
{
if (m==30)  
{
m=3;
}
else if(m==31)  
{
m=3;
}
else if(m==32)  
{
m=3;
}
delay(100);
lcd.clear();
}
}

//Обработка нажатия кнопки вниз
if (buttonnextWasUp && !buttonnextIsUp)
{
delay(10);
buttonnextIsUp = digitalRead(nextPin);
if (!buttonnextIsUp)
{
m++;
if (m>5)
{
m=0;
}
delay(100);
lcd.clear();
}
}

//Обработка нажатия кнопки вверх
if (buttonprevWasUp && !buttonprevIsUp)
{
delay(10);
buttonprevIsUp = digitalRead(prevPin);
if (!buttonprevIsUp)
{
m--;
if (m<0)
{
m=5;
}
delay(100);
lcd.clear();
}
}

// Обработка нажатия для р1 +
if (buttonupWasUp && !buttonupIsUp)
{
delay(10);
buttonupIsUp = digitalRead(upPin);
if (!buttonupIsUp && m==1)
{
p1++;
 if (p1>10)
{
p1=0;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р1 -
if (buttondownWasUp && !buttondownIsUp)
{
delay(10);
buttondownIsUp = digitalRead(downPin);
if (!buttondownIsUp && m==1)
{
p1--;
if (p1<0)
{
p1=10;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р2 +
if (buttonupWasUp && !buttonupIsUp)
{
delay(10);
buttonupIsUp = digitalRead(upPin);
if (!buttonupIsUp && m==2)
{
p2++;
if (p2>10)
{
p2=0;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р2 -
if (buttondownWasUp && !buttondownIsUp)
{
delay(10);
buttondownIsUp = digitalRead(downPin);
if (!buttondownIsUp && m==2)
{
p2--;
if (p2<0)
{
p2=10;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

//вывод меню

if (m==0)
{
lcd.setCursor(0, 0);
lcd.print( "Main Menu    " );
lcd.setCursor(0, 1);
lcd.print( "I1=" );
lcd.print(p1);
lcd.print( " U1=" );
lcd.print(p2);
lcd.print( " P1=" );
lcd.print(p3);
}

else if (m==1)
{
lcd.setCursor(0, 0);
lcd.print( "Time" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Measurement" );
}

else if (m==2)
{
lcd.setCursor(0, 0);
lcd.print( "Measurement" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Setpoint trip" );
}

else if (m==3)
{
lcd.setCursor(0, 0);
lcd.print( "Setpoint trip" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Configuration");
}

else if (m==4)
{
lcd.setCursor(0, 0);
lcd.print( "Configuration" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Registration");
}

else if (m==5)
{
lcd.setCursor(0, 0);
lcd.print( "Registration");
lcd.setCursor(14, 0);
lcd.print( "<-" );
}

if (m==30)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ1" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "MTZ2" );
}

else if (m==31)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ2" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "MTZ3" );
}

else if (m==32)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ3" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
}

buttonupWasUp = buttonupIsUp;
buttondownWasUp = buttondownIsUp;
buttonnextWasUp = buttonnextIsUp;
buttonprevWasUp = buttonprevIsUp;
buttonenterWasUp = buttonenterIsUp;
buttonescWasUp = buttonescIsUp;
}

(13 баллов) 1 5
исправил

2 Ответы

+3 голосов
 
Лучший ответ

Как я понял , вы кодирует в десятках числа номер 1 уровня ,а в еденицах второго. Реализация двухуровневого меню для "m" числа уровнй меню и "n(m)" числа подуровней подменю зависящего от выбранного меню должна включать в себя:

  1.  массив наибольшего кол-ва подуровней для каждого уровня 
    const byte ml[m]={1,1,3,1,5,7}
    //ml от max level
  2. Переменную хранящую в себе информацию на уровнях или подуровнях мы находимся 
bool lvst=1;// или true
//lvst сокр. level status

Логика работы.

Если lvst=1 меняем переменную m если 0 то n, также можно прописать что при изменении lvst n становилось равно 1. Где 1 означает первый подуровень , так при выходе из одного меню со значением n =5 и заходе в другое с максимальным кол-во подуровней 1,мы не будем видеть пустой экран до первого нажатия.

Если же lvst=0 то меняем переменную m, отвечающую за номер уровня.

 

#include <LiquidCrystal_I2C.h>            // подключаем библиотеку дисплея
#include <Wire.h>                       // подключаєм библиотеку
LiquidCrystal_I2C lcd (0x3F,16,2);        // указиваем тип дисплея (конвектора)

int m=0; // переменная для экранов меню
int p1=0; // переменная  1
int p2=0; // переменная  2
int p3=0; // переменная  3

#define nextPin 46 // кнопка меню на 6 входе ENTER
#define prevPin 42 // кнопка возврата ESC
#define upPin 48 // увеличение значения отображаемого параметра +
#define downPin 44 // уменьшение значения -
#define enterPin 52
#define escPin 50

boolean buttonnextWasUp = true;
boolean buttonprevWasUp = true;
boolean buttonupWasUp = true;
boolean buttondownWasUp = true;
boolean buttonenterWasUp = true;
boolean buttonescWasUp = true;

boolean ledEnabled = false;
byte n=0;
bool lvst=0;
const byte ml []={1,1,3,1}
//кол-во элементов массива 
//равно количеству уровней
void setup()
{

// Установка пинов как входов

pinMode(nextPin, INPUT_PULLUP);
pinMode(prevPin, INPUT_PULLUP);
pinMode(upPin, INPUT_PULLUP);
pinMode(downPin, INPUT_PULLUP);
pinMode(enterPin, INPUT_PULLUP);
pinMode(escPin, INPUT_PULLUP);

Serial.begin(9600);
lcd.init();            // инициализация LCD
lcd.backlight();       // включаем подсветку

// Вывод приветствия
lcd.print( "MY PROGECT" );
// задержка
delay (1000);
}

void loop ()
{
  boolean buttonnextIsUp = digitalRead(nextPin);
  boolean buttonprevIsUp = digitalRead(prevPin);
  boolean buttonupIsUp = digitalRead(upPin);
  boolean buttondownIsUp = digitalRead(downPin);
  boolean buttonenterIsUp = digitalRead(enterPin);
  boolean buttonescIsUp = digitalRead(escPin);

//обработка нажатия кнопки enter
if (buttonenterWasUp && !buttonenterIsUp)
{
delay(10);
buttonenterIsUp = digitalRead(enterPin);
if (!buttonenterIsUp)
{
if (lvst==0)
{
lvst=1;
n=0;
}
delay(100);
lcd.clear();
}
}

// обработка нажатия кнопки esc
if (buttonescWasUp && !buttonescIsUp)
{
delay(10);
buttonescIsUp = digitalRead(escPin);
if (!buttonescIsUp)
{
if (lvst==1)  
{
lvst=0;
}
delay(100);
lcd.clear();
}
}

//Обработка нажатия кнопки вниз
if (buttonnextWasUp && !buttonnextIsUp)
{
delay(10);
buttonnextIsUp = digitalRead(nextPin);
if (!buttonnextIsUp )
{ 
if(!lvst){  //н
m++;        //о
if (m>5)    //в
{           //ы
m=0;}       //й
else {      //код
n++
if(n>ml[m])n=0;
}

}
delay(100);
lcd.clear();
}
}
//также с вверх 
//Обработка нажатия кнопки вверх
if (buttonprevWasUp && !buttonprevIsUp)
{
delay(10);
buttonprevIsUp = digitalRead(prevPin);
if (!buttonprevIsUp)
{
m--;
if (m<0)
{
m=5;
}
delay(100);
lcd.clear();
}
}

// Обработка нажатия для р1 +
if (buttonupWasUp && !buttonupIsUp)
{
delay(10);
buttonupIsUp = digitalRead(upPin);
if (!buttonupIsUp && m==1)
{
p1++;
 if (p1>10)
{
p1=0;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р1 -
if (buttondownWasUp && !buttondownIsUp)
{
delay(10);
buttondownIsUp = digitalRead(downPin);
if (!buttondownIsUp && m==1)
{
p1--;
if (p1<0)
{
p1=10;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р2 +
if (buttonupWasUp && !buttonupIsUp)
{
delay(10);
buttonupIsUp = digitalRead(upPin);
if (!buttonupIsUp && m==2)
{
p2++;
if (p2>10)
{
p2=0;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

// Обработка нажатия для р2 -
if (buttondownWasUp && !buttondownIsUp)
{
delay(10);
buttondownIsUp = digitalRead(downPin);
if (!buttondownIsUp && m==2)
{
p2--;
if (p2<0)
{
p2=10;
}
delay (100);
lcd.setCursor(5, 1);
lcd.print("  ");
}
}

//вывод меню

if (m==0)
{
lcd.setCursor(0, 0);
lcd.print( "Main Menu    " );
lcd.setCursor(0, 1);
lcd.print( "I1=" );
lcd.print(p1);
lcd.print( " U1=" );
lcd.print(p2);
lcd.print( " P1=" );
lcd.print(p3);
}

else if (m==1)
{
lcd.setCursor(0, 0);
lcd.print( "Time" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Measurement" );
}

else if (m==2)
{
lcd.setCursor(0, 0);
lcd.print( "Measurement" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Setpoint trip" );
}

else if (m==3)
{
lcd.setCursor(0, 0);
lcd.print( "Setpoint trip" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Configuration");
}

else if (m==4)
{
lcd.setCursor(0, 0);
lcd.print( "Configuration" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "Registration");
}

else if (m==5)
{
lcd.setCursor(0, 0);
lcd.print( "Registration");
lcd.setCursor(14, 0);
lcd.print( "<-" );
}

if (m==30)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ1" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "MTZ2" );
}

else if (m==31)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ2" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
lcd.setCursor(0, 1);
lcd.print( "MTZ3" );
}

else if (m==32)
{
lcd.setCursor(0, 0);
lcd.print( "MTZ3" );
lcd.setCursor(14, 0);
lcd.print( "<-" );
}

buttonupWasUp = buttonupIsUp;
buttondownWasUp = buttondownIsUp;
buttonnextWasUp = buttonnextIsUp;
buttonprevWasUp = buttonprevIsUp;
buttonenterWasUp = buttonenterIsUp;
buttonescWasUp = buttonescIsUp;
}

(2.5 тыс. баллов) 5 15 38
выбран
Очень хороший способ, но не совсем понятно как реализовать  эти под уровни. Через  оператор swich? Как в этом варианте?

void updateMenu() {
  switch (menu) {
    case 0:
      menu = 1;
      break;
    case 1:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print( "Time" );
    lcd.setCursor(14, 0);
    lcd.print( "<-" );
    lcd.setCursor(0, 1);
    lcd.print( "Measurement   " );
      break;
    case 2:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print( "Measurement   " );
    lcd.setCursor(14, 0);
    lcd.print( "<-" );
    lcd.setCursor(0, 1);
    lcd.print( "Setpoint trip" );
      break;
    case 3:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print( "Setpoint trip" );
    lcd.setCursor(14, 0);
    lcd.print( "<-" );
    lcd.setCursor(0, 1);
    lcd.print( "Configuration");
      break;
    case 4:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print( "Configuration" );
    lcd.setCursor(14, 0);
    lcd.print( "<-" );
    lcd.setCursor(0, 1);
    lcd.print( "Registration");
      break;
    case 5:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print( "Registration");
    lcd.setCursor(14, 0);
    lcd.print( "<-" );
    lcd.setCursor(0, 1);
    lcd.print( "               ");
      menu = 4;
      break;
  }
}
0 голосов

Посмотрите на то, как реализовано в этой статье: клик — возможно, поможет.

(1.6 тыс. баллов) 2 9 33
Добро пожаловать на Бредборд! Сайт вопросов и ответов на тему Arduino, Raspberry Pi и хоббийной электроники в целом. Цель Бредборда — быть максимально полезным. Поэтому мы строго следим за соблюдением правил, боремся с холиворами и оффтопиком.
  1. parovoZZ

    70 балл(ов)

  2. Un_ka

    64 балл(ов)

  3. sser

    38 балл(ов)

  4. Тигрыч

    10 балл(ов)

  5. Kiprais

    9 балл(ов)

  6. N08I40K

    8 балл(ов)

Награды месяца
1-е место: Raspberry Pi 3 Model A+
2-е место: Raspberry Pi Zero W
3-е место: Raspberry Pi Zero

...