Skocz do zawartości

Proste menu


OLi_m

Pomocna odpowiedź

Jako początkujący w programowaniu ( od 3 m-cy , w wolnych chwilach ) chciałem się podzielić moją b. skromną wiedzą w temacie MENU . Zajmuję się teraz drugim projektem, równocześnie dopiero ucząc się od podstaw programowania. Pierwszym był prosty układ NANO + 2xLDC 7segm. pokazujący w moim wiekowym aucie numer wybranego przez sterownik skrzyni biegu, automatycznej skrzyni biegów. Obecny projekt ( zaawansowany w ok. 40% działającego ! kodu i na bieżąco sprawdzanego w miniaturowym "pseudo-modelu" maszyny ), to sterownik elektrycznej tarczowej przecinarki do glazury/gresów itp (na własne potrzeby). Ma / będzie mieć kilka programów , ustawień , zabezpieczeń PINem itp.  Ale wracając do meritum - niewiele brakowało abym na samym początku  poddał się z braku właśnie odpowiedniego i w miarę prostego menu. Niby, długo szukając można się natknąć w internecie na różne z tym, że zawsze było jakieś ale... .  

W końcu znalazłem na YT: https://www.youtube.com/watch?v=USWxPxPcDVA  opart na module keypad shield:

/*
==================== https://www.youtube.com/watch?v=USWxPxPcDVA =====================


/*
  Code for LCD shield by Saptashisb Das
  Menu Driven Program
  Tutorial 1
*/

#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);  

int keypad_pin = A0;
int keypad_value = 0;
int keypad_value_old = 0;

char btn_push;

byte mainMenuPage = 1;
byte mainMenuPageOld = 1;
byte mainMenuTotal = 4;

void setup()
{
    lcd.begin(16,2);  //Initialize a 2x16 type LCD

    MainMenuDisplay();  //  wyświetla główne menu
    delay(1000);
}
void loop()
{
    btn_push = ReadKeypad();   //   odczyt klawiatury

    MainMenuBtn();

    if(btn_push == 'S')//enter selected menu  .
    {
        WaitBtnRelease();
        switch (mainMenuPage)    //  przełącznik / główna strona menu
        {
            case 1:
              MenuA();
              break;
            case 2:
              MenuB();
              break;
            case 3:
              MenuC();
              break;
            case 4:
              MenuD();
              break;
        }

          MainMenuDisplay();
          WaitBtnRelease();
    }



    delay(10);

}//--------------- End of loop() loop ---------------------

void MainMenuDisplay()     // główne menu
{
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Wybierz menu");
    lcd.setCursor(1,1);
    switch (mainMenuPage)
    {
        case 1:
          lcd.print("1. Menu A");
          break;
        case 2:
          lcd.print("2. Menu B");
          break;
        case 3:
          lcd.print("3. Menu C");
          break;
        case 4:
          lcd.print("4. Menu D");
          break;
    }
}

void MenuA()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Inside Menu A");

    while(ReadKeypad()!= 'L')
    {
        //Insert Task for Menu A here

    }
}
void MenuB()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Inside Menu B");

    while(ReadKeypad()!= 'L')
    {
        //Insert Task for Menu B here

    }
}
void MenuC()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Inside Menu C");

    while(ReadKeypad()!= 'L')
    {
        //Insert Task for Menu C here

    }
}
void MenuD()
{  
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Inside Menu D");

    while(ReadKeypad()!= 'L')
    {
        //Insert Task for Menu D here

    }
}



void MainMenuBtn()
{
    WaitBtnRelease();
    if(btn_push == 'U')
    {
        mainMenuPage++;
        if(mainMenuPage > mainMenuTotal)
          mainMenuPage = 1;
    }
    else if(btn_push == 'D')
    {
        mainMenuPage--;
        if(mainMenuPage == 0)
          mainMenuPage = mainMenuTotal;    
    }

    if(mainMenuPage != mainMenuPageOld) //only update display when page change
    {
        MainMenuDisplay();
        mainMenuPageOld = mainMenuPage;
    }
}

char ReadKeypad()
{
  /* Keypad button analog Value
  no button pressed 1023
  select  741
  left    503
  up      326
  down    142
  right   0
  */
  keypad_value = analogRead(keypad_pin);

  if(keypad_value < 100)
    return 'R';
  else if(keypad_value < 200)
    return 'D';
  else if(keypad_value < 400)
    return 'U';
  else if(keypad_value < 600)
    return 'L';
  else if(keypad_value < 800)
    return 'S';
  else
    return 'N';

}

void WaitBtnRelease()
{
    while( analogRead(keypad_pin) < 800){}
}

Na własne potrzeby przerobiłem go na standardową klawiaturę 4x4 i wyświetlacz - na I2C:

/*
============ w oparciu o: https://www.youtube.com/watch?v=USWxPxPcDVA =====================

*/

#include <Keypad_I2C.h>
#include <Wire.h>
#define I2caddress 0x38  // adres PCF8574
const byte ROWS = 4;     // number of rows
const byte COLS = 4;     // number of columns
char keys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

byte rowPins[ROWS] = { 0, 1, 2, 3 };  //connect to the row pinouts of the keypad
byte colPins[COLS] = { 4, 5, 6, 7 };     //connect to the column pinouts of the keypad

Keypad_I2C klawiatura = Keypad_I2C(makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2caddress);

#include <LiquidCrystal_I2C.h>  // dolaczenie pobranej biblioteki I2C dla LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

byte mainMenuPage = 1;
byte mainMenuPageOld = 1;
byte mainMenuTotal = 4;


volatile int klawisz;  // klawisz naciśnięty w danej chwili

void setup() {
  Serial.begin(9600);
  klawiatura.begin();
  lcd.begin(16, 2);  //Initialize a 2x16 type LCD
  lcd.init();
  lcd.backlight();      // zalaczenie podwietlenia
  lcd.setCursor(0, 0);  // Ustawienie kursora w pozycji 0,0


  MainMenuDisplay();  //  wyświetla główne menu
  delay(1000);


}
void loop() {
  klawisz = klawiatura.getKey();

  MainMenuBtn();


  if (klawisz == '#')  //enter selected menu
  {
    WaitBtnRelease();
    switch (mainMenuPage)  //  przełącznik / główna strona menu
    {
      case 1:
        MenuA();
        break;
      case 2:
        MenuB();
        break;
      case 3:
        MenuC();
        break;
      case 4:
        MenuD();
        break;
    }

    MainMenuDisplay();
    WaitBtnRelease();
  }

  delay(10);

}  //--------------- koniec loop ---------------------

void MainMenuDisplay()  // główne menu
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Wybor menu:");
  lcd.setCursor(1, 1);
  switch (mainMenuPage) {
    case 1:
      lcd.print("1. Menu A");
      break;
    case 2:
      lcd.print("2. Menu B");
      break;
    case 3:
      lcd.print("3. Menu C");
      break;
    case 4:
      lcd.print("4. Menu D");
      break;
  }
}

void MenuA() {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Menu A");

  while (klawisz != '*')  
  {  
    klawisz = klawiatura.getKey();  // zapewnia obsługę klawiatury I2C w pętli while
     //Wstaw zadanie dla menu A tutaj
  }
}
void MenuB() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Menu B");

  while (klawisz != '*') 
  {
    klawisz = klawiatura.getKey();  // zapewnia obsługę klawiatury I2C w pętli while
    //Wstaw zadanie dla menu B tutaj
  }
}
void MenuC() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Menu C");

  while (klawisz != '*') 
  {
    klawisz = klawiatura.getKey();  // zapewnia obsługę klawiatury I2C w pętli while
    //Wstaw zadanie dla menu C tutaj
  }
}
void MenuD() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Menu D");

  while (klawisz != '*') 
  {
    klawisz = klawiatura.getKey();  // zapewnia obsługę klawiatury I2C w pętli while
   //Wstaw zadanie dla menu D tutaj
  }
}

void MainMenuBtn() {
  WaitBtnRelease();
  if (klawisz == '8')   // przewijanie menu w dół
  {
    mainMenuPage++;
    if (mainMenuPage > mainMenuTotal)
      mainMenuPage = 1;
  } else if (klawisz == '2')   // przewijanie menu w górę
  {
    mainMenuPage--;
    if (mainMenuPage == 0)
      mainMenuPage = mainMenuTotal;
  }

  if (mainMenuPage != mainMenuPageOld)  //  aktualizuje wyświetl. tylko po zmianie
  {
    MainMenuDisplay();
    mainMenuPageOld = mainMenuPage;
  }
}
void WaitBtnRelease() {}

Dla osób początkujących  jak ja, było to samodzielnie nie do przejścia. A dzięki takiemu gotowcowi jest motywacja żeby już samodzielnie drążyć temat dalej. Czego wszystkim "świeżakom" w nadchodzącym 2023r. życzę. 

Pozdrawiam

Edytowano przez OLi_m
  • Lubię! 2
Link do komentarza
Share on other sites

Niestety jak wejdziesz w menu to zatrzymujesz działanie programu. Tego rodzaju menu mają bardzo wąskie zastosowanie, właściwie tylko jako demo działania menu w ogóle.

Jak już ogarnąłeś ten rodzaj, to teraz sobie napisz takie, w którym podzielisz zadania w loop na bloki,np.: odczyt wejść, wysterowanie wyjść, jakieś obliczenia, obsługa menu, obsługa ekranu. W bloku obsługa ekranu co określony czas lub po zdarzeniu wymagającym aktualizacji wyświetl nowe dane na ekran. Jeśli jest aktywne menu to zamiast wyświetlania bieżących parametrów z działania programu wyświetl dane menu w kontekście miejsca, gdzie użytkownik zawędrował klikaniem przycisków. Czyli program ma normalnie dalej działać, a tylko informacje na ekranie są wyświetlane inne niż normalnie. Jeśli w czasie aktywnego menu program w bloku 'odczyt wejść'  odnotuje jakiś przycisk, to w bloku obsługa menu następuję wymagana akcja w kontekście gdzie jest użytkownik, czy jest w menu głównym, podmenu, czy w czasie aktywnego zmieniania parametrów. Jeśli np. ktoś naciśnie strzałkę w dół podczas nieaktywnego menu to ma się zmienić ekran na inny bo np. nie wszystkie parametry mieszczą się na jednym, a jeśli jest w menu to ma zmienić aktualnie wybrane, a jeśli edytujesz pozycję to ma zmienić wartość zmiennej w tej pozycji. Jak ekran jest jeden to każde naciśnięcie jakiegoś przycisku może aktywować stan menu. A jak nikt nie dotknie przycisku to też trzeba zareagować i wyjść z menu po jakimś timeout.

Nie możesz przestać sterować dawkowaniem paliwa, zapłonem, oknami, obsługiwać czujników zbliżeniowych, abs, wspomagania hamowania, bo ktoś kliknął wyszukiwanie stacji radiowej na pulpicie auta.

Albo włączyło grzałkę w inkubatorze, user kliknął zmianę temperatury, zadzwonił telefon i zamiast kurczaczków będzie miał kacze embriony smażone w sosie tamaryndowym.

  • Lubię! 1
Link do komentarza
Share on other sites

Z góry sorki za terminologię - to dla mnie nowość.
Ten szkic menu z pewnością nie był demo, faktem jest że jak wcześniej przeglądałem internet to natykałem się na całkiem fajne szkice menu, które nie miały czasu zajmować się niczym innym poza sobą 🙂 . Zauważ, że tutaj działania wykonywane są poprzez switch-case w odrębnych void-ach, a całe bloki przeróżnych funkcji w ramach pętli while umieszczonych w tych poszczególnych void-ach ( obecnie mam 9 pozycji menu ) . Dla mojego zastosowania zaletą jest przypisanie np. klawiatury i działania do aktualnego poziomu menu i wyjście z niego kończy ich działanie w tej konfiguracji.  U mnie prawidłowo to działa - reaguje na najróżniejsze polecenia , odczyt stanów krańcówek , wysterowania przekaźników,  wypełnienia PWM itp.. 
Właśnie wczoraj skończyłem podmenu z opcją zmiany, przy pomocy s-pinu, PIN-u (blokującego wejście do programu w maszynie)  z zapisem / odczytem z eeprom. 
Mam pełną świadomość tego, że  "rasowy" programista rwałby włosy z głowy na widok mojego szkicu, ale dla mnie ważne, że to prawidłowo działa. Poza tym będąc na poziomie "pierwszo-klasisty" nie mam szans wskoczyć od razu na poziom "studenta".  I w tym miejscu moja prośba do admina  - może warto rozważyć wprowadzenie 1-2 lekcji kursu poświęconego zasadom konstruowaniu jakiegoś uniwersalnego MENU ?, patrząc na ilość wejść na wcześniejsze posty z tym tematem w tytule zainteresowanie jest chyba spore.

Link do komentarza
Share on other sites

Problem jest w tym, że każdy tutaj ma swoją koncepcję na menu i jak zacznie się dyskusja, to dopiero będziesz miał mętlik w głowie 🙂

O ile mnie pamięć nie myli ten temat już przerabialiśmy...

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

2 godziny temu, ethanak napisał:

Problem jest w tym, że każdy tutaj ma swoją koncepcję na menu i jak zacznie się dyskusja, to dopiero będziesz miał mętlik w głowie 🙂

O ile mnie pamięć nie myli ten temat już przerabialiśmy...

No...

W temacie nieco ucichło. Ale pewne prace zostały wykonane. 

Cholera, dużo czasu i pracy w to włożyłem a wciąż końca nie widać. 

 

Edytowano przez _LM_
  • Lubię! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.