Помогите с LCD меню

djominov
Offline
Зарегистрирован: 23.09.2013

Доброго времени суток, друзья!

Недавно наткнулся на код одного из форумчан, yul-i-an. Так вот, запустил я его, всё класно, но мерцает экран, может кто смог бы подсказать где поставить перерисовку экрана, чтобы она не заставляла его постоянно мерцать.

Буду крайне благодарен, если кто сможет подсказать, как значения, которые выбираются в меню можно писать в EEPROM.

Всем откликнувшимся огромное спасибо!

Далее привожу пример кода, наверное это самое внятное меню, которое я пока встречал.

1 //Пример простого меню для Arduino
002 //В меню используется 4 экрана
003 //за номер отображаемого экрана отвечает переменная m
004 //Значения переменных р1,р2 меняются циклически от 0-10 затем опять 0, р3(LED) только 0 или 1
005  
006 #include <LiquidCrystal.h> //Библиотека LCD
007 // инициализация LCD
008 LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
009 int m=0; //переменная для экранов меню
010 int p1=0; // переменная для переменной 1
011 int p2=0; // -//- 2
012 int p3=0; // -//- 3
013  
014 byte nextPin = 6; //кнопка NEXT на 6 входе
015 byte prevPin = 7; //кнопка PREV
016 byte upPin = 9; //увеличение значения (UP) отображаемого параметра
017 byte downPin = 10; //уменьшение значения (DOWN)
018 byte ledPin =13; //Светодиод (исполнительное устройство)
019  
020 long previousMillis = 0; //счетчик прошедшего времени для AutoMainScreen
021 long interval = 3000; //задержка автовозврата к MainScreen 3сек
022  
023 void setup() {
024   //Настройка входов
025   pinMode(nextPin, INPUT);
026   pinMode(prevPin, INPUT);
027   pinMode(upPin, INPUT);
028   pinMode(downPin, INPUT);
029   //Настройка выходов
030   pinMode(ledPin, OUTPUT);
031   //Настройка дисплея
032   //Установка количества столбцов и строк дисплея
033   lcd.begin(16, 2);
034   // Вывод приветствия при включении питания если нужно
035   lcd.setCursor(3, 0);
036   lcd.print("DEMO MENU");
037   delay (300);//Задержка приветствия
038 }
039  
040 void loop() {
041   unsigned long currentMillis = millis();
042   //Обработка нажатия кнопки NEXT
043   if (digitalRead(nextPin)== HIGH)
044   {
045   m++;//увеличиваем переменную уровня меню
046   previousMillis = currentMillis; //если кнопка была нажата сбросить счетчик автовозврата к главному экрану
047   delay (100);
048   //lcd.clear();
049   if (m>3)//если уровень больше 3
050   {
051   m=0;// то вернуться к началу
052   }
053   }
054   //Обработка нажатия кнопки PREV
055   if (digitalRead(prevPin)== HIGH)
056   {
057   m--;
058   previousMillis = currentMillis;
059   delay (100);
060   //lcd.clear();
061   if (m<0)
062   {
063   m=3;
064   }
065   }
066   
067   //Обработка нажатия UP для р1
068     if (digitalRead(upPin)== HIGH && m==1)//если находимся на экране с переменной р1
069   {
070   p1++;      //то при нажатии кнопки + увеличиваем переменную р1 на единицу
071   previousMillis = currentMillis;
072   delay (100);
073   //lcd.clear();
074   if (p1>10) //устанавливаем придел изменения переменной = 10
075   {          //если больше предела
076   p1=0;      //то возвращаем ее к 0 (тут код условия что делать при достижении приделов)
077   }
078   }
079   //UP для р2
080     if (digitalRead(upPin)== HIGH && m==2)
081   {
082   p2++;
083   previousMillis = currentMillis;
084   delay (100);
085   //lcd.clear();
086   if (p2>10)
087   {
088   p2=0;
089   }
090   }
091     //UP для р3
092     if (digitalRead(upPin)== HIGH && m==3)
093   {
094   p3++;
095   previousMillis = currentMillis;
096   delay (100);
097   //lcd.clear();
098   if (p3>1)
099   {
100   p3=0;
101   }
102   digitalWrite(ledPin, p3);
103   }
104   //сдесь код для уменьшения значений
105   //аналогичен коду увеличения если нужно
106   
107   //Вывод меню
108   //Описание экранов меню
109   lcd.clear();
110   if (m==0)                 //переменная m=0
111   {                         //отображаем
112   lcd.setCursor(3, 0);      //*******************
113   lcd.print("Main Screen"); //*Main Screen      *
114   lcd.setCursor(0, 1);      //*P1=p1 P2=p2 LED=0*
115   lcd.print("P1=");         //*******************   
116   lcd.print(p1);
117   lcd.print(" P2=");
118   lcd.print(p2);
119   lcd.print(" LED=");
120   lcd.print(p3);
121   }
122   else if (m==1)            //переменная m=1
123   {                         //отображаем
124   lcd.setCursor(0, 0);      //*******************
125   lcd.print("Parametr-1");  //*Parametr-1       *
126   lcd.setCursor(0, 1);      //*P1=p1            *
127   lcd.print("P1 = ");       //*******************
128   lcd.print(p1);
129   }
130  else if (m==2)             //переменная m=2
131   {                         //отображаем
132   lcd.setCursor(0, 0);      //*******************
133   lcd.print("Parametr-2");  //*Parametr-2       *
134   lcd.setCursor(0, 1);      //*P2=p2            *
135   lcd.print("P2 = ");       //*******************
136   lcd.print(p2);
137   }
138  else if (m==3)             //переменная m=3
139   {                         //отображаем
140   lcd.setCursor(0, 0);      //*******************
141   lcd.print("LED Control"); //LED Control       *
142   lcd.setCursor(0, 1);      //LED = p3          *
143   lcd.print("LED = ");      //*******************
144   lcd.print(p3);
145   }
146   //Проверка автовозврата
147   if(currentMillis - previousMillis > interval)  //Если счетчик
148   {
149     previousMillis = currentMillis;              //достиг интервала
150     m=0;                                         //то отобразить главный экран
151     //lcd.clear();
152   }
153 //yul-i-an@gmail.com форум allduino.forum2x2.ru
154 }
 

 

djominov
Offline
Зарегистрирован: 23.09.2013
//Пример простого меню для Arduino
//В меню используется 4 экрана
//за номер отображаемого экрана отвечает переменная m
//Значения переменных р1,р2 меняются циклически от 0-10 затем опять 0, р3(LED) только 0 или 1 

#include <LiquidCrystal.h> //Библиотека LCD
// инициализация LCD
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int m=0; //переменная для экранов меню
int p1=0; // переменная для переменной 1
int p2=0; // -//- 2
int p3=0; // -//- 3

byte nextPin = 6; //кнопка NEXT на 6 входе
byte prevPin = 7; //кнопка PREV
byte upPin = 9; //увеличение значения (UP) отображаемого параметра
byte downPin = 10; //уменьшение значения (DOWN)
byte ledPin =13; //Светодиод (исполнительное устройство)

long previousMillis = 0; //счетчик прошедшего времени для AutoMainScreen
long interval = 3000; //задержка автовозврата к MainScreen 3сек

void setup() {
  //Настройка входов
  pinMode(nextPin, INPUT);
  pinMode(prevPin, INPUT);
  pinMode(upPin, INPUT);
  pinMode(downPin, INPUT);
  //Настройка выходов
  pinMode(ledPin, OUTPUT);
  //Настройка дисплея
  //Установка количества столбцов и строк дисплея
  lcd.begin(16, 2);
  // Вывод приветствия при включении питания если нужно
  lcd.setCursor(3, 0);
  lcd.print("DEMO MENU");
  delay (300);//Задержка приветствия
}

void loop() {
  unsigned long currentMillis = millis();
  //Обработка нажатия кнопки NEXT
  if (digitalRead(nextPin)== HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  previousMillis = currentMillis; //если кнопка была нажата сбросить счетчик автовозврата к главному экрану
  delay (100);
  //lcd.clear();
  if (m>3)//если уровень больше 3
  {
  m=0;// то вернуться к началу
  }
  }
  //Обработка нажатия кнопки PREV
  if (digitalRead(prevPin)== HIGH)
  {
  m--;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (m<0)
  {
  m=3;
  }
  }
  
  //Обработка нажатия UP для р1
    if (digitalRead(upPin)== HIGH && m==1)//если находимся на экране с переменной р1
  {
  p1++;      //то при нажатии кнопки + увеличиваем переменную р1 на единицу
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p1>10) //устанавливаем придел изменения переменной = 10
  {          //если больше предела
  p1=0;      //то возвращаем ее к 0 (тут код условия что делать при достижении приделов)
  }
  }
  //UP для р2
    if (digitalRead(upPin)== HIGH && m==2)
  {
  p2++;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p2>10)
  {
  p2=0;
  }
  }
    //UP для р3
    if (digitalRead(upPin)== HIGH && m==3)
  {
  p3++;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p3>1)
  {
  p3=0;
  }
  digitalWrite(ledPin, p3);
  }
  //сдесь код для уменьшения значений
  //аналогичен коду увеличения если нужно
  
  //Вывод меню
  //Описание экранов меню
  lcd.clear();
  if (m==0)                 //переменная m=0
  {                         //отображаем
  lcd.setCursor(3, 0);      //******************* 
  lcd.print("Main Screen"); //*Main Screen      *
  lcd.setCursor(0, 1);      //*P1=p1 P2=p2 LED=0*
  lcd.print("P1=");         //*******************    
  lcd.print(p1);
  lcd.print(" P2=");
  lcd.print(p2);
  lcd.print(" LED=");
  lcd.print(p3);
  } 
  else if (m==1)            //переменная m=1
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Parametr-1");  //*Parametr-1       *
  lcd.setCursor(0, 1);      //*P1=p1            *
  lcd.print("P1 = ");       //*******************
  lcd.print(p1);
  }
 else if (m==2)             //переменная m=2
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Parametr-2");  //*Parametr-2       *
  lcd.setCursor(0, 1);      //*P2=p2            *
  lcd.print("P2 = ");       //*******************
  lcd.print(p2);
  }
 else if (m==3)             //переменная m=3
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("LED Control"); //LED Control       *
  lcd.setCursor(0, 1);      //LED = p3          *
  lcd.print("LED = ");      //*******************
  lcd.print(p3);
  }
  //Проверка автовозврата
  if(currentMillis - previousMillis > interval)  //Если счетчик
  {
    previousMillis = currentMillis;              //достиг интервала 
    m=0;                                         //то отобразить главный экран
    //lcd.clear();
  }
//yul-i-an@gmail.com форум allduino.forum2x2.ru
}

В первом посте похоже запорол исходный код, вставил криво.

Клапауций
Offline
Зарегистрирован: 10.02.2013

djominov пишет:

Так вот, запустил я его, всё класно, но мерцает экран, может кто смог бы подсказать где поставить перерисовку экрана, чтобы она не заставляла его постоянно мерцать.

Ясно - всё хорошо, но плохо.

не вникая в суть программы можно сделать вывод, что автор пытался избавиться от мерцания(//lcd.clear();), но не осилил.

по сути:

- lcd.clear() нужно использовать, тогда когда необходимо очистить весь экран.

- применение delay (100) тоже маловразумительно, может и не влияет на мерцания, но тормозит выполнение программы, что не есть правильно.

djominov
Offline
Зарегистрирован: 23.09.2013

В том то и вопрос, delay по сути подвешивает весь процесс на какое то время. Из моих догадок - надо каким то образом повесить lcd.clear только там, где у нас происходят нажатие на кнопки, в том числе и при изменении параметров.

Клапауций буду крайне благодарен если поможете асилить новичку сей код.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Код не смотрел, но, есть варианты не чистить экран, а выделить в памяти место под буфер для вывода и обновлять этот буфер, а потом его выводить, тогда и чистить экран не нужно.

djominov
Offline
Зарегистрирован: 23.09.2013

А вам доводилось делать меню?

На самом деле я ищу вариант с наименее запутанной структурой и простотой использования. Может есть идеи, примеры?

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Не вижу проблем, DI HALT эту тему (отображение информации на лсд экран) разжевал вполне достаточно, чтобы этим вопросом не париться.
Потому я и говорю, пишем информацию в буфер, а потом весь буфер отображаем. Чему тут моргать то?

Клапауций
Offline
Зарегистрирован: 10.02.2013

ну, так и делайте c единственной поправкой - если вам не нужно очищать экран, то не нужно этого делать, если вам кажется, что вам нужно очистить экран, то возможно вам нужно перезаписать экран или перезаписать один или несколько символов.

все записи в экран производить не в общем теле программы, а по условию изменения чего-либо - изменения переменной, нажатия на кнопку, предыдущие состояния переменных и состояний кнопок хранить в оперативке.

можно использовать следующие конструкции - типо подпрограммы:

// для кнопки
void BUTTON() {
int nb = digitalRead(3);
if (nb != b) {
b = nb;
if (digitalRead(3) == LOW) {что-то делаем при нажатии кнопки - здесь можно стереть экран, если нужно;}
if (digitalRead(3) == HIGH) {что-то делаем при отпускании кнопки - здесь можно стереть экран, если нужно;}
}
}

 

// для переменной, обобщённый вариант.
void TEMP() {
int nb = (источник переменной, датчик или что ещё);
if (nb != b) {
b = nb;
if (digitalRead(3) == LOW) { }
if (digitalRead(3) == HIGH) { }
}
}
*не забыть сделать int b; в начале текста скетча.
djominov
Offline
Зарегистрирован: 23.09.2013

Спасибо большое, будем мучать.

А у вас нет примеров ваших меню из проектов?

И что можете сказать про применение EEPROM для хранения данных?

djominov
Offline
Зарегистрирован: 23.09.2013

В общем получилось немного оптимизировать код на свой манер. Единственное что не нравится - как реагируют кнопки на нажатие. Иногда может пропустить значение, тоесть как двойное нажатие срабатывает. Насколько помню, это как то лечится с помощью bounce. Но я честно говоря дикий нуб в этом деле, поэтому может кто поможет поправить код. Ещё не получилось прописать кнопку down, чтобы значение можно было уменьшать :( Что ещё хочу - избавиться от delay.

Вот что вышло:

//Пример простого меню для Arduino
//В меню используется 4 экрана
//за номер отображаемого экрана отвечает переменная m
//Значения переменных р1,р2 меняются циклически от 0-10 затем опять 0, р3(LED) только 0 или 1 

#include <Wire.h>  
#include <LCD.h>  
#include <LiquidCrystal_I2C.h>  
  
#define LCD_I2C_ADDR    0x27  // Define I2C Address where the PCF8574A is  
#define BACKLIGHT     15  //PIN that turn on backlight
#define LCD_EN  2  
#define LCD_RW  1  
#define LCD_RS  0  
#define LCD_D4  4  
#define LCD_D5  5  
#define LCD_D6  6  
#define LCD_D7  7  

// инициализация LCD
LiquidCrystal_I2C       lcd(LCD_I2C_ADDR,LCD_EN,LCD_RW,LCD_RS,LCD_D4,LCD_D5,LCD_D6,LCD_D7);  
int m=0; //переменная для экранов меню
int p1=0; // переменная для переменной 1
int p2=0; // -//- 2
int p3=0; // -//- 3

byte nextPin = 34; //кнопка NEXT на 6 входе
byte prevPin = 32; //кнопка PREV
byte upPin = 30; //увеличение значения (UP) отображаемого параметра
byte downPin = 28; //уменьшение значения (DOWN)
byte ledPin =13; //Светодиод (исполнительное устройство)

long previousMillis = 0; //счетчик прошедшего времени для AutoMainScreen
long interval = 5000; //задержка автовозврата к MainScreen 3сек

void setup() {
  //Настройка входов
  pinMode(nextPin, INPUT);
  pinMode(prevPin, INPUT);
  pinMode(upPin, INPUT);
  pinMode(downPin, INPUT);
  //Настройка выходов
  pinMode(ledPin, OUTPUT);
  //Настройка дисплея
  //Установка количества столбцов и строк дисплея
  lcd.begin(20, 4);
  // Вывод приветствия при включении питания если нужно
  lcd.setCursor(3, 0);
  lcd.print("DEMO MENU");
  delay (300);//Задержка приветствия
}

void loop() {
  unsigned long currentMillis = millis();
  //Обработка нажатия кнопки NEXT
  if (digitalRead(nextPin)== HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  lcd.clear(); 
  previousMillis = currentMillis; //если кнопка была нажата сбросить счетчик автовозврата к главному экрану
  delay (100);
  
  if (digitalRead(nextPin)== LOW)
  {
  lcd.clear(); 
  }
  if (m>3)//если уровень больше 3
  {
  m=0;// то вернуться к началу
  }
  }
  
  //Обработка нажатия кнопки PREV
  if (digitalRead(prevPin)== HIGH)
  {
  m--;
  previousMillis = currentMillis;
  delay (100);
 
  if (digitalRead(prevPin)== LOW)
  {
  lcd.clear(); 
  }
  //lcd.clear();
  if (m<0)
  {
  m=3;
  }
  }
  
  //Обработка нажатия UP для р1
    if (digitalRead(upPin)== HIGH && m==1)//если находимся на экране с переменной р1
  {
  p1++;      //то при нажатии кнопки + увеличиваем переменную р1 на единицу
  previousMillis = currentMillis;
  delay (100);
  if (digitalRead(upPin)== LOW)
  {
  lcd.clear(); 
  }
  //lcd.clear();
  if (p1>10) //устанавливаем придел изменения переменной = 10
  {          //если больше предела
  p1=0;      //то возвращаем ее к 0 (тут код условия что делать при достижении приделов)
  }
  }
  //UP для р2
    if (digitalRead(upPin)== HIGH && m==2)
  {
  p2++;
  previousMillis = currentMillis;
  delay (100);
  if (digitalRead(upPin)== LOW)
  {
  lcd.clear(); 
  }
  //lcd.clear();
  if (p2>10)
  {
  p2=0;
  }
  }
    //UP для р3
    if (digitalRead(upPin)== HIGH && m==3)
  {
  p3++;
  previousMillis = currentMillis;
  delay (100);
  if (digitalRead(upPin)== LOW)
  {
  lcd.clear(); 
  }
  if (p3>1)
  {
  p3=0;
  }
  digitalWrite(ledPin, p3);
  }
  //сдесь код для уменьшения значений
  //аналогичен коду увеличения если нужно
  
  //Вывод меню
  //Описание экранов меню
  if (m==0)                 //переменная m=0
  {    //отображаем
  lcd.setCursor(3, 0);      //******************* 
  lcd.print("Main Screen"); //*Main Screen      *
  lcd.setCursor(0, 1);      //*P1=p1 P2=p2 LED=0*
  lcd.print("P1=");         //*******************    
  lcd.print(p1);
  lcd.print(" P2=");
  lcd.print(p2);
  lcd.print(" LED=");
  lcd.print(p3);
  } 
  else if (m==1)            //переменная m=1
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Parametr-1");  //*Parametr-1       *
  lcd.setCursor(0, 1);      //*P1=p1            *
  lcd.print("P1 = ");       //*******************
  lcd.print(p1);
//  lcd.clear();
  }
 else if (m==2)             //переменная m=2
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Parametr-2");  //*Parametr-2       *
  lcd.setCursor(0, 1);      //*P2=p2            *
  lcd.print("P2 = ");       //*******************
  lcd.print(p2);
//  lcd.clear();
  }
 else if (m==3)             //переменная m=3
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("LED Control"); //LED Control       *
  lcd.setCursor(0, 1);      //LED = p3          *
  lcd.print("LED = ");      //*******************
  lcd.print(p3);
//  lcd.clear();
  }
  //Проверка автовозврата
    if(currentMillis - previousMillis > interval && m !=0)  //Если счетчик
    {
    previousMillis = currentMillis;              //достиг интервала 
    lcd.clear();
    m=0;                                         //то отобразить главный экран
     }

}

 

djominov
Offline
Зарегистрирован: 23.09.2013

Клапауций

Может могли бы поглядеть мой код, сказать где что поправить. Спасибо заранее!

Клапауций
Offline
Зарегистрирован: 10.02.2013

djominov пишет:

Клапауций

Может могли бы поглядеть мой код, сказать где что поправить. Спасибо заранее!

я не программист совсем - радиомонтажник

поправить могу посоветовать только аппаратно железную часть и архитектурно программый код (в меру своего понимания концепции меню девайса)

- конденсаторы 0,1 микрофарад на кнопки, что бы избавиться от паразитного дребега некачественных кнопок. двойные нажатия останутся, но не будут настолько непредсказуемыми и раздражающими, можно программно затупить ввод, но тогда будет раздражать невозможность быстрого ввода параметров/переключения меню. но лучше изначально решать эту проблему на этапе построения логической блок-схемы работы всега. к примеру - лучше, что бы некие параметры изменялись при нажатии или, наоборот, при отпускании кнопки.

- меню есть индикация работы девайса (не более), весь основной код должен работать корректно даже, если полностью закомментировать код меню. Тогда всё становится проще - на этапе тестирования кода индицируем на один экран все переменные: служебные переменные меню(номера меню и подменю), переменные программ, переменные, которые поступают на девайс, прочие. Рулите этими переменными с помощью кнопок, вылавливаете тараканов - долго, но просветляет суть своих хотелок. Только затем одеваете всё бохатство в красивые экраны - почему мигает будет понятно.

*сам лично - пока на этапе построения простого меню на двух энкодерах и одной кнопке(выбор экранов производится кручением одного энкодера при нажатой кнопке, манипуляции с переменными в пределах одного экрана производится энкодерами(простой вариант - две переменные, по одной на каждый энкодер, сложный вариант - одним энкодером выбираем переменную, вторым - ея изменяем), запоминание в оперативку изменений производим во время отпускания кнопки(одновременно пишем изменения состояния всего в ееепром). Публиковать код смысла не вижу - пока сам не знаю, чем это всё окончится - не желается, что бы пару неофитов потом мучились со странным.

djominov
Offline
Зарегистрирован: 23.09.2013

Спасибо за инфу, будет интересно поглядеть ваш код, когда доведёте до ума.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

Клапауций пишет:

*сам лично - пока на этапе построения простого меню на двух энкодерах и одной кнопке(выбор экранов производится кручением одного энкодера при нажатой кнопке, манипуляции с переменными в пределах одного экрана производится энкодерами(простой вариант - две переменные, по одной на каждый энкодер, сложный вариант - одним энкодером выбираем переменную, вторым - ея изменяем), запоминание в оперативку изменений производим во время отпускания кнопки(одновременно пишем изменения состояния всего в ееепром). Публиковать код смысла не вижу - пока сам не знаю, чем это всё окончится - не желается, что бы пару неофитов потом мучились со странным.

дружище, ты изобретаеш лисапед, причем очень кривой и страшный :)

возьми готовые решения.. когда крутиш ручку то передвигаешся по меню по кругу, а когда нажал на ручку то выбрал и першел на следующий уровень меню.. и количество кнопок уменьшится и срочки кода усохнут :)

Клапауций
Offline
Зарегистрирован: 10.02.2013

Puhlyaviy пишет:

дружище, ты изобретаеш лисапед, причем очень кривой и страшный :)

возьми готовые решения.. когда крутиш ручку то передвигаешся по меню по кругу, а когда нажал на ручку то выбрал и першел на следующий уровень меню.. и количество кнопок уменьшится и срочки кода усохнут :)

готовые решения - это где?, посоветуй топикстартеру - у него мигает халабуда.

*повторю описание своего велосипеда - пока не нашёл отличий от твоего описания невелосипеда:

аппаратно - два энкодера, одна кнопка.

когда кручу энкодер1 при нажатой кнопке, то переключаю экраны по кругу, когда просто кручу энкодер1, то переключаю выбор переменной в пределах одного экрана, энкодером2 меняю значение, выбранной энкодером1 переменной.

куда там ещё код сократить - не знаю: три служебных переменных меню(состояния энкодеров 1,2, состояние кнопки), состояния энкодеров запоминаются в еепром. согласно значениям этих трёх переменных происходит печать в индикатор.

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

оставь адын энкодер. нафига тебе их два то? и почему не три?

Клапауций
Offline
Зарегистрирован: 10.02.2013

Puhlyaviy пишет:

оставь адын энкодер. нафига тебе их два то? и почему не три?

точно - меню для бедных с одной кнопкой.

*я расписал подробно, зачем два энкодера - потому, что можно два, но не принципиально обязательно.

djominov
Offline
Зарегистрирован: 23.09.2013

Друзья, а с моим кодом мне больше никто не поможет? Энкодер кстати интересная идея.

Клапауций
Offline
Зарегистрирован: 10.02.2013

djominov пишет:

Друзья, а с моим кодом мне больше никто не поможет? Энкодер кстати интересная идея.

перепиши сам всё с нуля - будешь хоть понимать, что глючит.

xp-lu
Offline
Зарегистрирован: 15.10.2013

djominov,

Похоже у вас  неправильно собрана электрическая схема кнопок. 

У меня нет мерцания. 

 
nik
Offline
Зарегистрирован: 02.05.2013

Привет всем,

kisoft

помогите с этой темой, где можно почитать подробнее, можите сбросить
пару ссылок. А то облазил google и нечего не нашел.

" Не вижу проблем, DI HALT эту тему (отображение информации на лсд экран) разжевал вполне достаточно, чтобы этим вопросом не париться.
Потому я и говорю, пишем информацию в буфер, а потом весь буфер от
ображаем. Чему тут моргать то? "

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Di halt lcd - в гугле.
Найдете учебный курс, там всё есть

ingener.solovyev
Offline
Зарегистрирован: 12.02.2013

kisoft пишет:
Di halt lcd - в гугле. Найдете учебный курс, там всё есть

 

Вот тут по смотори

skyspirit
Offline
Зарегистрирован: 27.02.2015

Кто нибуть с таким экраном разбирался G128064-MH-R1 http://www.rcscomponents.kiev.ua/r46646.html

И что нужно знать о экране чтоб подключить к ардуино?

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

skyspirit пишет:

И что нужно знать о экране чтоб подключить к ардуино?

Нужно знать какой там контролер стоит. Дальше либо искать готовые библиотеки на данный контроллер, либо брать даташит и изучать. По вышеприведенной ссылке указан контроллер дисплея и дан даташит на сам дисплей, где приведена распиновка.

skyspirit
Offline
Зарегистрирован: 27.02.2015

Я добавил изминение в код для своего устройства которое откривает заслонку при определенных условиях температуры и давления, и эти параметры срабатывания можно менять нажатием кнопки UP и DOWN и, то сейчас меня интересует как можно сделать чтоб при дальнейшем включении были предидущие настройки срабатывания ???



#include <LiquidCrystal.h> //Библиотека LCD
#include <Servo.h> 
#include <BMP085.h>
#include <Wire.h>
Servo servo;
 
BMP085 dps = BMP085();
long Temperature = 0;
long Pressure = 0;
 
// инициализация LCD
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int m=0; //переменная для экранов меню
int p1=70; // переменная для переменной 1
int p2=750; // -//- 2
int p3=0; // -//- 3
 
byte nextPin = 12; //кнопка NEXT на 6 входе
byte prevPin = 7; //кнопка PREV
byte upPin = 9; //увеличение значения (UP) отображаемого параметра
byte downPin = 10; //уменьшение значения (DOWN)
byte ledPin =13; //Светодиод (исполнительное устройство)
 
long previousMillis = 0; //счетчик прошедшего времени для AutoMainScreen
long interval = 3000; //задержка автовозврата к MainScreen 3сек
 
void setup() {
  
   servo.attach(9);
   
  //Настройка входов
  pinMode(nextPin, INPUT);
  pinMode(prevPin, INPUT);
  pinMode(upPin, INPUT);
  pinMode(downPin, INPUT);
  //Настройка выходов
  pinMode(ledPin, OUTPUT);
  //Настройка дисплея
  //Установка количества столбцов и строк дисплея
  lcd.begin(16, 2);
  // Вывод приветствия при включении питания если нужно
  lcd.setCursor(3, 0);
  lcd.print("DEMO MENU");
  delay (300);//Задержка приветствия
}
 
void loop() {
  unsigned long currentMillis = millis();
  //Обработка нажатия кнопки NEXT
  if (digitalRead(nextPin)== HIGH)
  {
  m++;//увеличиваем переменную уровня меню
  previousMillis = currentMillis; //если кнопка была нажата сбросить счетчик автовозврата к главному экрану
   //delay (100);
  if (m>3)//если уровень больше 3
  {
  m=0;// то вернуться к началу
  }
  }
  //Обработка нажатия кнопки PREV
  if (digitalRead(prevPin)== HIGH)
  {
  m--;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (m<0)
  {
  m=3;
  }
  }
  
  //Обработка нажатия UP для р1
    if (digitalRead(upPin)== HIGH && m==0)//если находимся на экране с переменной р1
  {
  p1++;      //то при нажатии кнопки + увеличиваем переменную р1 на единицу
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p1>10) //устанавливаем придел изменения переменной = 10
  {          //если больше предела
  p1=0;      //то возвращаем ее к 0 (тут код условия что делать при достижении приделов)
  }
  }
  //UP для р2
    if (digitalRead(upPin)== HIGH && m==0)
  {
  p2++;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p2>10)
  {
  p2=0;
  }
  }
    //UP для р3
    if (digitalRead(upPin)== HIGH && m==3)
  {
  p3++;
  previousMillis = currentMillis;
  delay (100);
  //lcd.clear();
  if (p3>1)
  {
  p3=0;
  }
  digitalWrite(ledPin, p3);
  }
  //сдесь код для уменьшения значений
  //аналогичен коду увеличения если нужно
  
  //Вывод меню
  //Описание экранов меню
  lcd.clear();
  if (m==0)                 //переменная m=0
  {           
dps.getPressure(&Pressure);
dps.getTemperature(&Temperature);
 
 
    //отображаем
  lcd.setCursor(3, 0);      //******************* 
  lcd.print("Main Screen"); //*Main Screen      *
  lcd.setCursor(0, 1);      //*P1=p1 P2=p2 LED=0*
  lcd.print("Temperature=");         //*******************    
  lcd.print(p1);
  lcd.print("Pressure=");
  lcd.print(p2);
  lcd.print(" LED=");
  lcd.print(p3);
  lcd.print(Temperature*0.1,1);
  lcd.print(Pressure / 133.3,1);
  
  if (Temperature*0.1 > p1)
  if (Pressure / 133.3 < p2)
  {
    servo.write(150); //откритие заслонки
  }
  
  
  
  }  
  //Проверка автовозврата
  if(currentMillis - previousMillis > interval)  //Если счетчик
  {
    previousMillis = currentMillis;              //достиг интервала 
    m=0;                                         //то отобразить главный экран
    //lcd.clear();
  }
//yul-i-an@gmail.com форум allduino.forum2x2.ru
}

 

bankir_1986
Offline
Зарегистрирован: 23.03.2015

"Энергонезависимая память" она же EEPROM

skyspirit
Offline
Зарегистрирован: 27.02.2015

какую функцию использовать если мне нужно увеличить параметр на 10 или 20 едениц одним нажатием так как при такой (P1++) только на еденицу увеличивает?

bwn
Offline
Зарегистрирован: 25.08.2014

P1 = P1+10

skyspirit
Offline
Зарегистрирован: 27.02.2015

Благодарю, но лучше создать новую переменную как по мне  R1 = p1+10 или если нада больше R1 = p1*10  и подставлять R1 там где нужно изминять параметр, а то код неадекватно работает :)

bwn
Offline
Зарегистрирован: 25.08.2014

А потом к новой переменной будем 10 добавлять и так до бесконечности. Р1 в этом случае ведь не изменится.

skyspirit
Offline
Зарегистрирован: 27.02.2015

я разобрался в общем ;)

skyspirit
Offline
Зарегистрирован: 27.02.2015

Теперь вопрос стал разобратся с сохранением настроек и заданых параметров после последнего включения ардуино с использованием EEPROM библиотеки

bwn
Offline
Зарегистрирован: 25.08.2014

Раздел "Программирование" в самом низу. Там правда только для однобайтовых переменных. По многобайтовым, форум почитайте.

snega
Offline
Зарегистрирован: 31.10.2016
Моя версия меню, 
есть переменняе в коде которые надо менять, в данное меню передаются ссылки на эти переменные
Левой кнопкой заходим-выходим из меню
вверх - них   соотверственно  +  -
Правой перебираем айтемы по кругу


#include <Bounce.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

LiquidCrystal_I2C lcd(0x27,20,4);

#define BUTTON_LEFT 9
#define BUTTON_RIGHT 12
#define BUTTON_UP 10
#define BUTTON_DOWN 11


//переменные которые надо менять
int _V1_rastopka = 30; int _V1_rabota = 60; int _V1_zavershenie = 20;int _T_Rastopka = 50;int _T_Zavershenie = 40;int _T_Stop = 30;int _T_Avariya = 90;

//текущий пунет меню,  флаг отображения меню
int curMenu = 0; bool b_ShowmMenu = 0;
//кол пунктов меню
const int CountMenu = 7;

//создание кнопок через класс Bounce
Bounce bLeft = Bounce( BUTTON_LEFT, 5 ); Bounce bRight = Bounce( BUTTON_RIGHT, 5 ); Bounce bUp = Bounce( BUTTON_UP, 5 ); Bounce bDown = Bounce( BUTTON_DOWN, 5 ); 

//массив элементов меню
struct MENU_ITEM{ //
char name[17];
  int *val;
};

//инициализация меню
//строковое имя, адрес переменной которую надо менять
MENU_ITEM mMenu[CountMenu] = {
"V1_rastopka   1", &_V1_rastopka,
"V1_rabota     2", &_V1_rabota,
"V1_zavershen  3", &_V1_zavershenie,
"T_Rastopka    4", &_T_Rastopka,
"T_Zavershenie 5", &_T_Zavershenie,
"T_Stop        6", &_T_Stop,
"T_Avariya     7", &_T_Avariya
};


//функия выполнения меню
void menu(){
 //  bRight  //след пункт меню по кругу
if (bRight.update())
  if (bRight.read() == HIGH)
    if (curMenu == CountMenu - 1)
      curMenu = 0;
         else   curMenu++;

//  bLeft  выход из меню
if (bLeft.update())
  if (bLeft.read() == HIGH)
    b_ShowmMenu = 0;
    
//  bUp  +1 значение
if (bUp.update())
  if (bUp.read() == HIGH)
    (*mMenu[curMenu].val)++;

  
 //  bDown -1 зачение
if (bDown.update())
  if (bDown.read() == HIGH)
    (*mMenu[curMenu].val)--;


  //вывод использую буффур
  char ch[16];  
  snprintf(ch, 16, "%d         ", *mMenu[curMenu].val); 

  lcd.setCursor(0,0); lcd.print(mMenu[curMenu].name);
  lcd.setCursor(0,1); lcd.print(ch); 
}


void setup() {

  lcd.init();
  lcd.backlight(); 

  
  pinMode( BUTTON_LEFT, INPUT );
  pinMode( BUTTON_UP, INPUT );
  pinMode( BUTTON_DOWN, INPUT );
  pinMode( BUTTON_RIGHT, INPUT );
 
}


void loop() {

//вывод меню
  if (b_ShowmMenu) {
    menu();
  }else{
    if ( bLeft.update() )
      if ( bLeft.read() )
         b_ShowmMenu = 1;

  lcd.clear();
  }
}

 

fanps
Offline
Зарегистрирован: 22.04.2016

вот мой доработанный код. Экран не мерцает. В качестве экрана и кнопок использовал LCD Keypad Shield. 

Экран обновляется по взведенному флагу. Если флаг не взведен, то экран не обновляется.

//Пример простого меню для Arduino
//В меню используется 4 экрана
//за номер отображаемого экрана отвечает переменная m
//Значения переменных р1,р2 меняются циклически от 0-10 затем опять 0, р3(LED) только 0 или 1 

#include <LiquidCrystal.h> //Библиотека LCD
// инициализация LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int m=0; //переменная для экранов меню
int p1=0; // переменная для переменной 1
int p2=6; // -//- 2 время возврата к главному экрану от 2 до 9 сек
int p3=0; // -//- 3
int f=1; // флаг обновления экрана
int k=0; // флаг нажатия кнопки для возврата на главный экран

byte ledPin =13; //Светодиод (исполнительное устройство)

long previousMillis = 0; //счетчик прошедшего времени для AutoMainScreen
long interval = p2*1000; //задержка автовозврата к MainScreen 6 сек


void setup() {
  
 
  //Настройка выходов
  pinMode(ledPin, OUTPUT);
  //Настройка дисплея
  //Установка количества столбцов и строк дисплея
  lcd.begin(16, 2);
  // Вывод приветствия при включении питания если нужно
  lcd.setCursor(3, 0);
  lcd.print("DEMO MENU");
  delay (3000);//Задержка приветствия
}

void loop() {              //кнопки
   int x; 
  x = analogRead (0); 

  long interval = p2*1000; //задержка автовозврата к MainScreen сек
   
  unsigned long currentMillis = millis();
  //Обработка нажатия кнопки NEXT
  if (x < 60 && x>0)
  {
  m++;//увеличиваем переменную уровня меню
  previousMillis = currentMillis; //если кнопка была нажата сбросить счетчик автовозврата к главному экрану
  delay (500);
  //lcd.clear();
  if (m>3)//если уровень больше 3
  {
  m=0;// то вернуться к началу
  }
  f=1;
  k=1;
  }
  //Обработка нажатия кнопки PREV
  if (x < 600 && x>400)
  {
  m--;
  previousMillis = currentMillis;
  delay (500);
  //lcd.clear();
  if (m<0)
  {
  m=3;
  }
  f=1;
  k=1;
  }
  
  //Обработка нажатия UP для р1
    if (x < 200 && x >60 && m==1)//если находимся на экране с переменной р1
  {
  p1++;      //то при нажатии кнопки + увеличиваем переменную р1 на единицу
  previousMillis = currentMillis;
  delay (500);
  //lcd.clear();
  if (p1>10) //устанавливаем придел изменения переменной = 10
  {          //если больше предела
  p1=0;      //то возвращаем ее к 0 (тут код условия что делать при достижении приделов)
  }
  f=1;
  k=1;
  }
  //UP для р2
    if (x < 200 && x >60 && m==2)
  {
  p2++;
  previousMillis = currentMillis;
  delay (500);
  //lcd.clear();
  if (p2>9)
  {
  p2=2;
  }
  f=1;
  k=1;
  }
    //UP для р3
    if (x < 200 && x >60 && m==3)
  {
  p3++;
  previousMillis = currentMillis;
  delay (500);
  //lcd.clear();
  if (p3>1)
  {
  p3=0;
  }
  f=1;
  k=1;
  digitalWrite(ledPin, p3);
  }
  //сдесь код для уменьшения значений
  //аналогичен коду увеличения если нужно
  
  //Вывод меню
  //Описание экранов меню
  if (f==1) {
  lcd.clear();
  if (m==0)                 //переменная m=0
  {                         //отображаем
  lcd.setCursor(3, 0);      //******************* 
  lcd.print("Main Screen"); //*Main Screen      *
  lcd.setCursor(0, 1);      //*P1=p1 P2=p2 LED=0*
  lcd.print("P1=");         //*******************    
  lcd.print(p1);
  lcd.print(" Pau=");
  lcd.print(p2);
  lcd.print(" LED=");
  lcd.print(p3);
  } 
  else if (m==1)            //переменная m=1
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Parametr-1");  //*Parametr-1       *
  lcd.setCursor(0, 1);      //*P1=p1            *
  lcd.print("P1 = ");       //*******************
  lcd.print(p1);
  }
 else if (m==2)             //переменная m=2
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("Pause");       //*Parametr-2       *
  lcd.setCursor(0, 1);      //*P2=p2            *
  lcd.print("Pau = ");       //*******************
  lcd.print(p2);
  }
 else if (m==3)             //переменная m=3
  {                         //отображаем
  lcd.setCursor(0, 0);      //*******************
  lcd.print("LED Control"); //LED Control       *
  lcd.setCursor(0, 1);      //LED = p3          *
  lcd.print("LED = ");      //*******************
  lcd.print(p3);
  }
  f=0;
  }
  //Проверка автовозврата
  if(currentMillis - previousMillis > interval && k==1)  //Если счетчик
  {
    previousMillis = currentMillis;              //достиг интервала 
    m=0;                                         //то отобразить главный экран
    //lcd.clear();
    f=1;
    k=0;
  }


}

 

satelit 2
Offline
Зарегистрирован: 04.12.2016

всех приветствую, у меня проблема, решить не могу, в логику не укладывается

для наладки переменных использую монитор порта, в котором выводятся значения переменных. еще стоит дисплей от нокиа 3310, который должен будет остаться в конечном варианте. в мониторе порта переменные выводяться правильные. а на дисплее переменная которая управляет шимом( переменная равна шиму) не корректна до 100, после 100 соответсвует действительности(сравниваю по результатам монитора порта и свечением диода). 


//ВВЕРХ-БЕЛЫЙ, ВЛЕВО -КРАСНЫЙ, ВНИЗ-ЗЕЛЕНЫЙ,ВПРАВО-СИНИЙ
//+ЯРКОСТЬ-ЯРКОСТЬ, SCRL F ВЫКЛ
#include <IRremote.h> // это скачанная библиотека
#include <PCD8544.h>
static const byte glyph[] = { B00010000, B00110100, B00110000, B00110100, B00010000 };


static PCD8544 lcd;

int r=0;//
int g=0;//
int b=0;//
int x=1;//ручной выбор цвета1-7
boolean R=false; // Флаг вывода индикатора
boolean G=false;
boolean B=false;
boolean W=false;// флаг белого
boolean X=false;//режим выбора цвета

int RECV_PIN = 11; //вход ИК приемника
IRrecv irrecv(RECV_PIN);
decode_results results;
              void setup(){
   lcd.begin(84, 48);
  
  // Add the smiley to position "0" of the ASCII table...
  lcd.createChar(0, glyph);
                
                Serial.begin (9600);
  Serial.println("Hello");
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(6, OUTPUT);
  irrecv.enableIRIn();    } // включить приемник 
              void loop() {
 static int counter = 0;
                analogWrite(9,r); 
analogWrite(10,g);
analogWrite(6,b);
if (irrecv.decode(&results)) {
delay(300); 
 Serial.println("power");

if (results.value == 0xB54A50AF){// если нажали кнопку +
    if (R == true) {r=r+15;}
    if (G == true) {g=g+15;}
    if (B == true) {b=b+15;}
    if (W == true) {r=r+15; g=g+15; b=b+15;}
    if (X == true) {x=x+1;}


}


if (results.value == 0xB54AD02F)// если нажали кнопку -
   { 
    if (R == true) {r=r-15;}
    if (G == true) {g=g-15;}
    if (B == true) {b=b-15;}
    if (W == true) {r=r-15; g=g-15; b=b-15;}
    if (X == true) {x=x-1;}
 }
   
    
    
if (results.value == 0xB54A58A7)// R
   {b=LOW;  g=LOW; r=255; R=true; G=false; B=false; W=false; X=false; }
if (results.value == 0xB54A1AE5) // G
   {b=LOW;  r=LOW; g=255; R=false; G=true; B=false;W=false;  X=false; }
if (results.value == 0xF50AE649) //B
   {r=LOW;  g=LOW; b=255; R=false; G=false; B=true; W=false; X=false; }
if (results.value == 0xB54A48B7) //W БЕЛЫЙ
   {b=255;  g=255; r=255; R=false; G=false; B=false; W=true; X=false;   }
if (results.value == 0xB54A30CF) //Х РАДУГА 
   {b=LOW;  g=LOW; r=LOW; R=false; G=false; B=false; W=false; X=true;   }  
if (X == true && x==1) {r=255; g=100; b=0; }//красный
if (X == true && x==2) {r=255; g=100; b=0; }//оранжевый
if (X == true && x==3) {r=255; g=100; b=0; }//желтый
if (X == true && x==4) {r=255; g=100; b=0; }//зеленвй
if (X == true && x==5) {r=255; g=100; b=0; }//голубой
if (X == true && x==6) {r=255; g=100; b=0; }//синий
if (X == true && x==7) {r=255; g=100; b=0; }//фиолетовый

if (results.value == 0xB54A9867) //ВЫКЛ 
   {b=LOW;  g=LOW; r=LOW; } 

if (r>255) {r=255;}
if (r<0) {r=0;}
if (g>255) {g=255;}
if (g<0) {g=0;}
if (b>255) {b=255;}
if (b<0) {b=0;}
if (x>7) {x=1;}
if (x<1) {x=7;}
Serial.print("R");
Serial.print(R);
Serial.print("G");
Serial.print(G);
Serial.print("B");
Serial.print(B);
Serial.print("W");
Serial.println(W);
Serial.print("R");
Serial.print(r);
Serial.print("G");
Serial.print(g);
Serial.print("B");
Serial.println(b);
Serial.print("X");
Serial.println(x);
 lcd.setCursor(0, 0);
  lcd.print("NISSAN");
   lcd.setCursor(0, 1);
  lcd.print(r);
lcd.setCursor(0, 2);
  lcd.print(g);
delay(50); //

irrecv.resume(); // 

}


}

 

satelit 2
Offline
Зарегистрирован: 04.12.2016

ураааа. эксперементы рулят. дописал по ln и все показания корректны

lcd.println(r);

f1ili1n
Offline
Зарегистрирован: 22.01.2017

Почему в выводе на экран перед числом стоит "0"? Как исправить???

satelit 2
Offline
Зарегистрирован: 04.12.2016

код в студию, у меня все четко

tsivicov
Offline
Зарегистрирован: 20.11.2017

что такое кнопка NEXT

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Добрый день. Такой к вам имеется вопрос.

На сайте http://winavr.scienceprog.com/example-avr-projects/avr-lcd-menu-routine.html нашел пример меню на LCD дисплее и atmega8. Четырьмя кнопками задается режим работы 3 светодиодов. В архиве кроме файлов проекта есть файл симуляции протеуса. Проверил - работает как надо. 

Решил попробывать просимулировать для ардуино - пролема в том что не происходит опроса кнопки, точнее условие по нажатию не срабатывает. Опрос в исходном коде(mega8) идет в теле обработчика прерывания таймера 0 каждые 122 раза в секунду(Гц). Таймер 0 в среде ардуино системный поэтому я сделал аналогично на таймере 2 - 100 раз в секунду. В  остальном практически нет изменений. 

menu.c (mega8)

//*****************************************************************************
//
// File Name	: 'menu.c'
// Title		: Two level menu demo
// Author		: Scienceprog.com - Copyright (C) 2007
// Created		: 2007-03-23
// Revised		: 2007-03-23
// Version		: 1.0
// Target MCU	: Atmel AVR series
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "lcd_lib.h"

#define UP 0
#define DOWN 3
#define LEFT 1
#define RIGHT 2
#define LED_R 5
#define LED_G 4
#define LED_B 3
#define BT_PORT PORTC
#define BT_DDR  DDRC
#define BT_PIN  PINC
#define LED_PORT PORTB
#define LED_DDR DDRB

typedef void (*FuncPtr)(void);
//function pointer
FuncPtr FPtr;
//Structure describes current menu and submenu state
struct Menu_State{
	 uint8_t menuNo;//1,2,3,4
	 uint8_t subMenuNo;//1,2,3
}MN;
//flag required for exiting from functions loops
uint8_t Flag=1;
//Menu Strings in flash
const uint8_t MN000[] PROGMEM="Menu Demo\0";
//menu 1
const uint8_t MN100[] PROGMEM="<<One Led>>\0";
//menu 1 submenus
const uint8_t MN101[] PROGMEM="R ON\0";
const uint8_t MN102[] PROGMEM="G ON\0";
const uint8_t MN103[] PROGMEM="B ON\0";
//menu 2
const uint8_t MN200[] PROGMEM="<<All ON/OFF>>\0";
//Submenus of menu 2
const uint8_t MN201[] PROGMEM="All ON\0";
const uint8_t MN202[] PROGMEM="All OFF\0";
//menu 3
const uint8_t MN300[] PROGMEM="<<Auto scroll>>\0";
//Submenus of menu 3
const uint8_t MN301[] PROGMEM="Left\0";
const uint8_t MN302[] PROGMEM="Right\0";
//menu 4
const uint8_t MN400[] PROGMEM="<<Blink All>>\0";
//submenus of menu 4
const uint8_t MN401[] PROGMEM="Blink Fast\0";
const uint8_t MN402[] PROGMEM="Blink Slow\0";
//more menus and submenus can be added.
//Arrays of pointers to menu strings stored in flash
const uint8_t *MENU[] ={
		MN100,	//menu 1 string
		MN200,	//menu 2 string
		MN300,	//menu 3 string
		MN400	//menu 4 string
		};
const uint8_t *SUBMENU[] ={
		MN101, MN102, MN103,	//submenus of menu 1
		MN201, MN202,			//submenus of menu 2
		MN301, MN302,			//submenus of menu 3
		MN401, MN402			//submenus of menu 4
		};

//Menu structure
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 submenus
const uint8_t MSTR2[] PROGMEM ={
	4,	//number of menu items
	3,	//Number of submenu items of menu item 1
	2,	//of menu item 2
	2,	//of menu item 3
	2	//of menu item 4
	}; 
//Function prototypes
//Timer0 initialization
void Timer0_init(void);
//Initial menu
void menu_Init(void);
//Inits ports for buttons and LED's
void ports_Init(void);
//long delay (1s)
void delay1s(void);
//Functions for each menu item
void func101(void);
void func102(void);
void func103(void);
void func201(void);
void func202(void);
void func301(void);
void func302(void);
void func401(void);
void func402(void);


//Arrray of function pointers in Flash
const FuncPtr FuncPtrTable[] PROGMEM=
    { 	func101, func102, func103,	//functions for submenus of menu 1
		func201, func202, 			//functions for submenus of menu 2
		func301, func302, 			//functions for submenus of menu 3
		func401, func402			//functions for submenus of menu 4
		};


//SubMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//Timer0 overflow interrupt service routine	
ISR(TIMER0_OVF_vect)
{
	//if button UP pressed
	if (bit_is_clear(BT_PIN, UP))
	{
		if (MN.menuNo<pgm_read_byte(&MSTR2[0]))
		{ 
			MN.menuNo++;
			MN.subMenuNo=1;
		}
		else
		{
			MN.menuNo=1;
		}
		LCDclr();
		//Display menu item
		CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
		//Display submenu item
		CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
		//Assign function to function pointer
		FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);
		//set Flag to 0 menas menu have changed 
		Flag=0;
		//wait for button release
		loop_until_bit_is_set(BT_PIN, UP);
	}
	//if Button DOWN pressed
	if (bit_is_clear(BT_PIN, DOWN))
	{
		if (MN.menuNo==1)
		{ 
			MN.menuNo=pgm_read_byte(&MSTR2[0]);
			MN.subMenuNo=1;
		}
		else
		{
			MN.menuNo--;
		}
	LCDclr();
	//
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);
	Flag=0;
	loop_until_bit_is_set(BT_PIN, DOWN);
	}
	//If Button RIGHT pressed
	if (bit_is_clear(BT_PIN, RIGHT))
	{
		if (MN.subMenuNo<pgm_read_byte(&MSTR2[MN.menuNo]))
		{ 
			MN.subMenuNo++;
		}
		else
		{
			MN.subMenuNo=1;
		}
	LCDclr();
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);	
	Flag=0;
	loop_until_bit_is_set(BT_PIN, RIGHT);
	}
	//If button LEFT pressed
	if (bit_is_clear(BT_PIN, LEFT))
	{
		if (MN.subMenuNo==1)
		{ 
			MN.subMenuNo=pgm_read_byte(&MSTR2[MN.menuNo]);
		}
		else
		{
			MN.subMenuNo--;
		}
	LCDclr();
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);	
	Flag=0;
	loop_until_bit_is_set(BT_PIN, LEFT);
	}	
}

int main(void)
{
	LCDinit();
	LCDclr();
	LCDhome();
	//Welcome demo message
	CopyStringtoLCD(MN000, 0, 0 );
	delay1s();
	//Init Buttons and LEDs ports
	ports_Init();
	LCDclr();
	//Initial menu and initial function
	menu_Init();
	//init timer0 for key readings using overflow interrups
	Timer0_init();
	while(1)
	{
	//set flag to 1
	//when button menu changes flag sets to 0
	Flag=1;
	//execute function that is pointed by FPtr
	FPtr();
	}
	return 0;
}
void Timer0_init(void)
{
	// 8MHz ATmega8
	TCNT0=0x00;
	TCCR0|=(1<<CS02); //prescaller 256 ~122 interrupts/s
	TIMSK|=(1<<TOV0);//Enable Timer0 Overflow interrupts
	sei();
}
void menu_Init(void)
{
 	MN.menuNo=1;
	MN.subMenuNo=1;
	LCDclr();
	CopyStringtoLCD(MENU[(MN.menuNo-1)], 0, 0 );
	CopyStringtoLCD(SUBMENU[(MN.subMenuNo-1)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
void ports_Init(void)
{
 BT_PORT|=1<<UP|1<<DOWN|1<<LEFT|1<<RIGHT;//Pullups
 BT_DDR&=~(0<<UP|0<<DOWN|0<<LEFT|0<<RIGHT);//as input
 LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
 LED_DDR|=1<<LED_R|1<<LED_G|1<<LED_B;//Led pins as output
 
}
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
	uint8_t p=0;//points to menu in table of function pointer 
	for(uint8_t i=0; i<(mn-1); i++)
	{
		p=p+pgm_read_byte(&MSTR2[i+1]);
	}
	p=p+sb-1;
	return p;
}

void delay1s(void)
{
	uint8_t i;
	for(i=0;i<10;i++)
	{
		_delay_ms(10);
	}
}

void func101(void)
{
LED_PORT&=~(1<<LED_R);
LED_PORT|=1<<LED_G|1<<LED_B;//Led_R ON
}
void func102(void)
{
LED_PORT&=~(1<<LED_G);
LED_PORT|=1<<LED_R|1<<LED_B;//Led_G ON
}
void func103(void)
{
LED_PORT&=~(1<<LED_B);
LED_PORT|=1<<LED_R|1<<LED_G;//Led_B ON
}
void func201(void)
{
LED_PORT&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
}
void func202(void)
{
LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
}
void func301(void)
{
while(Flag)
{
	LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	delay1s();
	LED_PORT&=~(1<<LED_R);
	LED_PORT|=1<<LED_G|1<<LED_B;//Led_R ON
	delay1s();
	LED_PORT&=~(1<<LED_G);
	LED_PORT|=1<<LED_R|1<<LED_B;//Led_G ON
	delay1s();
	LED_PORT&=~(1<<LED_B);
	LED_PORT|=1<<LED_R|1<<LED_G;//Led_B ON
	delay1s();
}
}
void func302(void)
{
while(Flag)
{
	LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	delay1s();
	LED_PORT&=~(1<<LED_B);
	LED_PORT|=1<<LED_R|1<<LED_G;//Led_B ON	
	delay1s();
	LED_PORT&=~(1<<LED_G);
	LED_PORT|=1<<LED_R|1<<LED_B;//Led_G ON
	delay1s();
	LED_PORT&=~(1<<LED_R);
	LED_PORT|=1<<LED_G|1<<LED_B;//Led_R ON
	delay1s();
}
}
void func401(void)
{
while(Flag)
{
	LED_PORT&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
	delay1s();
	LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	delay1s();
}
}
void func402(void)
{
while(Flag)
{
	LED_PORT&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
	delay1s();
	delay1s();
	LED_PORT|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	delay1s();
	delay1s();
}
}

 

Моя аналогия с ошибкой

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
//------------------------------------------
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
//------------------------------------------
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
//------------------------------------------

#define UP 0
#define DOWN 3
#define LEFT 1
#define RIGHT 2

#define LED_R 2  
#define LED_G 1
#define LED_B 0

//------------------------------------------
typedef void (*FuncPtr)(void);
//function pointer
FuncPtr FPtr;
//Structure describes current menu and submenu state
struct Menu_State{
	 uint8_t menuNo;//1,2,3,4
	 uint8_t subMenuNo;//1,2,3
}MN;
//flag required for exiting from functions loops
uint8_t Flag=1;
//Menu Strings in flash
const uint8_t MN000[] PROGMEM="Menu Demo\0";
//menu 1
const uint8_t MN100[] PROGMEM="<<One Led>>\0";
//menu 1 submenus
const uint8_t MN101[] PROGMEM="R ON\0";
const uint8_t MN102[] PROGMEM="G ON\0";
const uint8_t MN103[] PROGMEM="B ON\0";
//menu 2
const uint8_t MN200[] PROGMEM="<<All ON/OFF>>\0";
//Submenus of menu 2
const uint8_t MN201[] PROGMEM="All ON\0";
const uint8_t MN202[] PROGMEM="All OFF\0";
//menu 3
const uint8_t MN300[] PROGMEM="<<Auto scroll>>\0";
//Submenus of menu 3
const uint8_t MN301[] PROGMEM="Left\0";
const uint8_t MN302[] PROGMEM="Right\0";
//menu 4
const uint8_t MN400[] PROGMEM="<<Blink All>>\0";
//submenus of menu 4
const uint8_t MN401[] PROGMEM="Blink Fast\0";
const uint8_t MN402[] PROGMEM="Blink Slow\0";
//more menus and submenus can be added.
//Arrays of pointers to menu strings stored in flash
const uint8_t *MENU[] ={
		MN100,	//menu 1 string
		MN200,	//menu 2 string
		MN300,	//menu 3 string
		MN400	//menu 4 string
		};
const uint8_t *SUBMENU[] ={
		MN101, MN102, MN103,	//submenus of menu 1
		MN201, MN202,			//подменю пунктам меню 2
		MN301, MN302,			//submenus of menu 3
		MN401, MN402			//submenus of menu 4
		};

//Menu structure
//[0] -Number of level 0 menu items
//[1]...[n] number of second level menu items
//Eg. MSTR2[1] shows that menu item 1 has 3 submenus
const uint8_t MSTR2[] PROGMEM ={
	4,	// количество пунктов меню
	3,	//Number of submenu items of menu item 1
	2,	//of menu item 2
	2,	//of menu item 3
	2	//of menu item 4
	}; 
//Function prototypes
//Timer2 initialization
void init_timer2(void);
//Initial menu
void menu_Init(void);
//Inits ports for buttons and LED's
void ports_Init(void);
//Functions for each menu item
void func101(void);
void func102(void);
void func103(void);
void func201(void);
void func202(void);
void func301(void);
void func302(void);
void func401(void);
void func402(void);


//Arrray of function pointers in Flash
const FuncPtr FuncPtrTable[] PROGMEM=
    { 	func101, func102, func103,	//functions for submenus of menu 1
	func201, func202, 		//functions for submenus of menu 2
	func301, func302, 		//functions for submenus of menu 3
	func401, func402		//functions for submenus of menu 4
};
//SubMenu and Function table pointer update
uint8_t MFIndex(uint8_t, uint8_t);
//------------------------------------------
//Timer0 overflow interrupt service routine	
ISR(TIMER2_COMPA_vect)
{
    //if button UP pressed
    if (bit_is_clear(PINB, UP))
    {
	if (MN.menuNo<pgm_read_byte(&MSTR2[0]))
	{ 
		MN.menuNo++;
		MN.subMenuNo=1;
	}
	else
	{
		MN.menuNo=1;
	}
	lcd.clear();
	//Display menu item
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	//Display submenu item
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	//Assign function to function pointer
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);
	//set Flag to 0 menas menu have changed 
	Flag=0;
	//wait for button release
	loop_until_bit_is_set(PINB, UP);
    }	
    //if Button DOWN pressed
    if (bit_is_clear(PINB, DOWN))
    {
	if (MN.menuNo==1)
	{ 
		MN.menuNo=pgm_read_byte(&MSTR2[0]);
		MN.subMenuNo=1;
	}
	else
	{
		MN.menuNo--;
	}
	lcd.clear();
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);
	Flag=0;
	loop_until_bit_is_set(PINB, DOWN);
    }
    //If Button RIGHT pressed
    if (bit_is_clear(PINB, RIGHT))
    {
	if (MN.subMenuNo<pgm_read_byte(&MSTR2[MN.menuNo]))
	{ 
		MN.subMenuNo++;
	}
	else
	{
		MN.subMenuNo=1;
	}
	lcd.clear();
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);	
	Flag=0;
	loop_until_bit_is_set(PINB, RIGHT);
    }
    //If button LEFT pressed
    if (bit_is_clear(PINB, LEFT))
    {
    	if (MN.subMenuNo==1)
    	{ 
    		MN.subMenuNo=pgm_read_byte(&MSTR2[MN.menuNo]);
    	}
    	else
    	{
    		MN.subMenuNo--;
    	}
    	lcd.clear();
    	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
    	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
    	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);	
    	Flag=0;
    	loop_until_bit_is_set(PINB, LEFT);
    }   		
}
//------------------------------------------
void setup() {
  lcd.begin(); // initialize the LCD
  lcd.backlight(); // Turn on the blacklight and print a message.
  lcd.home();
  //Welcome demo message
  CopyStringtoLCD(MN000, 0, 0 );
  _delay_ms(1000);
  lcd.clear();
  ports_Init();
  //Initial menu and initial function
  menu_Init();
  init_timer2();
  sei();
}
//------------------------------------------
void loop() { 
  while(1) {
    //set flag to 1
    //when button menu changes flag sets to 0
    Flag=1;
    //execute function that is pointed by FPtr
    FPtr();
  }
}

void init_timer2(void) 
{
  //Прерывание происходит каждые 10 мс
  TCCR2B=0; // таймер2 остановлен
  TCNT2=0; //счётный регистр обнулён
  OCR2B=0;
  TIFR2&=0xff; //отчистить флаги прерываний
  TCCR2A = (1<<WGM21);    // Режим CTC (сброс по совпадению)
  TCCR2B = (1<<CS20)|(1<<CS21)|(1<<CS22); // CLK/1024
  OCR2A = 155;
  TIMSK2 = (1<<OCIE2A);  // Разрешить прерывание по совпадению
}
//------------------------------------------
void CopyStringtoLCD(const uint8_t *FlashLoc, uint8_t x, uint8_t y)
{
	uint8_t i;
	lcd.setCursor(x,y);
	for(i=0;(uint8_t)pgm_read_byte(&FlashLoc[i]);i++)
	{
		lcd.write((uint8_t)pgm_read_byte(&FlashLoc[i]));
	}
}
//------------------------------------------
void ports_Init() {
  DDRB&=~(0<<UP|0<<DOWN|0<<LEFT|0<<RIGHT);//as input
  PORTB|=1<<UP|1<<DOWN|1<<LEFT|1<<RIGHT;//Pullups
  DDRC |=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
  PORTC |=1<<LED_R|1<<LED_G|1<<LED_B;//Led pins as output
}
//------------------------------------------
void menu_Init(void)
{
 	MN.menuNo=1;
	MN.subMenuNo=1;
	lcd.clear();
	CopyStringtoLCD(MENU[(MN.menuNo-1)], 0, 0 );
	CopyStringtoLCD(SUBMENU[(MN.subMenuNo-1)], 0, 1 );
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[0]);
}
//------------------------------------------
uint8_t MFIndex(uint8_t mn, uint8_t sb)
{
	uint8_t p=0;//points to menu in table of function pointer 
	for(uint8_t i=0; i<(mn-1); i++)
	{
		p=p+pgm_read_byte(&MSTR2[i+1]);
	}
	p=p+sb-1;
	return p;
}
//------------------------------------------
void func101(void)
{
PORTC&=~(1<<LED_R);
PORTC|=1<<LED_G|1<<LED_B;//Led_R ON
}
void func102(void)
{
PORTC&=~(1<<LED_G);
PORTC|=1<<LED_R|1<<LED_B;//Led_G ON
}
void func103(void)
{
PORTC&=~(1<<LED_B);
PORTC|=1<<LED_R|1<<LED_G;//Led_B ON
}
void func201(void)
{
PORTC&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
}
void func202(void)
{
PORTC|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
}
void func301(void)
{
while(Flag)
{
	PORTC|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	_delay_ms(1000);
	PORTC&=~(1<<LED_R);
	PORTC|=1<<LED_G|1<<LED_B;//Led_R ON
	_delay_ms(1000);
	PORTC&=~(1<<LED_G);
	PORTC|=1<<LED_R|1<<LED_B;//Led_G ON
	_delay_ms(1000);
	PORTC&=~(1<<LED_B);
	PORTC|=1<<LED_R|1<<LED_G;//Led_B ON
	_delay_ms(1000);
}
}
void func302(void)
{
while(Flag)
{
	PORTC|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	_delay_ms(1000);
	PORTC&=~(1<<LED_B);
	PORTC|=1<<LED_R|1<<LED_G;//Led_B ON	
	_delay_ms(1000);
	PORTC&=~(1<<LED_G);
	PORTC|=1<<LED_R|1<<LED_B;//Led_G ON
	_delay_ms(1000);
	PORTC&=~(1<<LED_R);
	PORTC|=1<<LED_G|1<<LED_B;//Led_R ON
	_delay_ms(1000);
}
}
void func401(void)
{
while(Flag)
{
	PORTC&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
	_delay_ms(1000);
	PORTC|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	_delay_ms(1000);
}
}
void func402(void)
{
while(Flag)
{
	PORTC&=~(1<<LED_R|1<<LED_G|1<<LED_B);//Leds ON
	_delay_ms(1000);
	_delay_ms(1000);
	PORTC|=1<<LED_R|1<<LED_G|1<<LED_B;//Leds OFF
	_delay_ms(1000);
	_delay_ms(1000);
}
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Не совсем понимаю смысл применения макроса loop_until_bit_is_set. Зачем ждать отпускания кнопки?

    //if button UP pressed
    if (bit_is_clear(PINB, UP))
    {
	if (MN.menuNo<pgm_read_byte(&MSTR2[0]))
	{ 
		MN.menuNo++;
		MN.subMenuNo=1;
	}
	else
	{
		MN.menuNo=1;
	}
	lcd.clear();
	//Display menu item
	CopyStringtoLCD(MENU[MN.menuNo-1], 0, 0 );
	//Display submenu item
	CopyStringtoLCD(SUBMENU[MFIndex(MN.menuNo, MN.subMenuNo)], 0, 1 );
	//Assign function to function pointer
	FPtr=(FuncPtr)pgm_read_word(&FuncPtrTable[MFIndex(MN.menuNo, MN.subMenuNo)]);
	//set Flag to 0 menas menu have changed 
	Flag=0;
	//wait for button release
	loop_until_bit_is_set(PINB, UP);
    }	

И прошу не делйте мне замечаний по поводу этого, никто не запрещает тут использовать while(1) {}

void loop() { 
  while(1) {
    //set flag to 1
    //when button menu changes flag sets to 0
    Flag=1;
    //execute function that is pointed by FPtr
    FPtr();
  }
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Вопрос снят