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

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

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • 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.