OLi_m Napisano Grudzień 27, 2022 Udostępnij Napisano Grudzień 27, 2022 (edytowany) 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 Grudzień 27, 2022 przez OLi_m 2 Cytuj Link do komentarza Share on other sites More sharing options...
kaczakat Grudzień 27, 2022 Udostępnij Grudzień 27, 2022 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. 1 Cytuj Link do komentarza Share on other sites More sharing options...
OLi_m Grudzień 28, 2022 Autor tematu Udostępnij Grudzień 28, 2022 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. Cytuj Link do komentarza Share on other sites More sharing options...
ethanak Grudzień 28, 2022 Udostępnij Grudzień 28, 2022 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... 1 Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. 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
_LM_ Grudzień 28, 2022 Udostępnij Grudzień 28, 2022 (edytowany) 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 Grudzień 28, 2022 przez _LM_ 1 Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
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!