Skocz do zawartości

Tworzenie prostego menu na lcd


Krawi92

Pomocna odpowiedź

Witam, chciałbym aby ktoś podpowiedział, jak podchodzić do tworzenia prostego menu w oparciu o wyswietlanie na lcd. Dziś w sumie udało mi się na lcd wyswietlać czas i datę w oparciu o PCF8583, ale chciałem zrobić opcje, aby użytkownik mógł go sobie samemu ustawić. W oparciu o 4 microswitche to zrobiłem i instrukcje switch:

while(1){
 
                if (!keylock1 && !(PIND & (1<<KEY1))){
                        keylock1=1;
                        licznik++;
                        if (licznik > 3)licznik=0;
                }else if (keylock1 && (PIND & (1<<KEY1)))keylock1++;
                switch(licznik){
                case 0:RTC_Event();
                        break;
                case 1:display_menu2(&czas); // tu wyświetla sie opcja ustaw date
                        break;
                case 2:display_menu(); // tu wyswietla sie opcja ustaw czas
                        break;
                case 3:zapisano_czas(); // tu zapisujemy do rtc dane ze struktury i komunikujemy uzytkownika ze zostala wszystko zapisane
                        break;
                }
 }

W ten prostacki sposób do tego podszedłem. Chciałbym jednak postarać się zrozumieć, jak rozbudowywuje się jakieś większe menu, gdzie ustawień może być więcej. Że za pomoca przycisków chodzimy po menu, wracamy do głównego itp. Czy jest to gdzieś jakos fajnie opisane od postaw? @_LM_ aktualnie prowadzi wpis, w którym tworzy taką bibliotekę, ale jestem jeszcze zbyt początkujący, żeby coś z tego zrozumieć 😄

Link do komentarza
Share on other sites

13 minut temu, Krawi92 napisał:

@_LM_ aktualnie prowadzi wpis, w którym tworzy taką bibliotekę, ale jestem jeszcze zbyt początkujący, żeby coś z tego zrozumieć 😄

Ooo panie u mnie to ledwo zalążek jest. 

 

15 minut temu, Krawi92 napisał:

Chciałbym jednak postarać się zrozumieć, jak rozbudowywuje się jakieś większe menu, gdzie ustawień może być więcej. Że za pomoca przycisków chodzimy po menu, wracamy do głównego itp. Czy jest to gdzieś jakos fajnie opisane od postaw?

Proponuję dowiedzieć się co to są struktury cykliczne, oraz listy jedno i dwukierunkowe. Skrótowo ten temat jest poruszony w książce Tomasza Francuza "Język C dla mikrokontrolerów AVR" Ale tam jest tylko wzmianka o tym.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Ja jako początkujący teź robiłem takie dość (jak na mnie) rozbudowane menu. Znajdziesz pewnie na Forbocie. Oparte wszystko na switch. Różne poziomy menu to oddzielne funkcje, a wewnątrz funkcji switch pozycje danego poziomu.

Generalnie bardzo pomaga, jak kiedyś koledzy podpowiedzieli, graf, maszyna stanów skończonych. Bardzo pomaga. Poczytaj.

 

Edytowano przez SOYER
  • Pomogłeś! 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

9 minut temu, Krawi92 napisał:

Czyli do jakiegoś prostego interfejsu nie jest wstydem korzystania po prostu ze switcha? 

A pewnie że nie 🙂 Jest tutaj artykuł kol @ethanak o maszynie stanów, doskonale nadaje się do prostych rozwiązań menu. A na moje wypociny w poście o którym wspomniałeś, póki nie będzie konkretnych rozwiązań, to nie patrz bo więcej sobie zaszkodzisz niż pomożesz.

Edytowano przez _LM_
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Ja sobie w wolnej chwili stukam biblioteczkę (aktualnie nie działa pod LCD, a tylko pod PC, ale przerobienie to kwestia napisania odpowiedniej klasy renderera 😉). Tworzenie menu:

menu_t* menu2  = (new menu_t())
        ->add_option(MENU_SIMPLE_OPTION("1. Tell me a joke \n", funny_text, action_type_t::ON_PRESS))
        ->add_option(MENU_BACK_OPTION("2. Back \n");

menu_t* menu1 = (new menu_t())
        ->add_option(MENU_SIMPLE_OPTION("1. Select option 2 \n", on_menu_select_2, action_type_t::ON_PRESS))
        ->add_option(MENU_SUBMENU_OPTION("2. More... \n", new submenu_action_t(menu2)))
        ->add_option(MENU_BACK_OPTION("3. Back \n");

menu_t* menu = (new menu_t())
        ->add_option(MENU_SIMPLE_OPTION("1. Select option 1 \n", on_menu_select_1, action_type_t::ON_PRESS))
        ->add_option(MENU_SUBMENU_OPTION("2. More... \n",
                                         new submenu_action_t(menu1)));

Oraz użycie: 😉 

menu_common_t::open_menu(menu);

Dla pytających:

typedef enum {
    ON_PRESS = 1,
    ON_RELEASE = 2,
    ON_HOLD = 4,
    ON_DECREASE = 8,
    ON_INCREASE = 16,
    ON_CHANGE = (ON_DECREASE | ON_INCREASE)
} action_type_t;

Używam action_type, bo pozwala mi na łatwe przesyłanie informacji do menu o tym co się stało w inpucie jednocześnie będąc od niego niezależnym 😉 Tylko to menu jest projektowane pod RP2040, gdzie RAM'u mam pod dostatkiem, więc się tym nie przejmuję... 😛 

A tutaj ten tragicznie napisany nieoptymalny kod dla chętnych:

menuLib.zip

UWAGA: W.I.P.

Link do komentarza
Share on other sites

Ciężko jest pisać na ekranie, więc odpowiem bardzo krótko - zamiast listy wystarczy tablica. A jej elementami moga być nazwy opcji w menu i funkcje do wywołania - wtedy nie potrzeba ani list, ani długich instrukcji switch.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

jeszcze spytam całkiem o coś innego, troszkę odejdę od tematu. Wkleję kod funkcji, który nie działał jak chciałem.

void display_menu2(){
	ustawiamy_date(); // Tu w formie funkcji wyświetlanie na LCD daty i tekstu "ustaw date"

	if (!keylock2 && !(PIND & (1<<KEY2))){ // Naciśnięcie klawisza zwiększa dni
		keylock2=1;
		czas.DD++;
		if (czas.DD > 31)czas.DD=0;
	}else if(keylock2 && (PIND & (1<<KEY2)))keylock2=0;

	if (!keylock3 && !(PIND & (1<<KEY3))){ // Naciśnięcie klawisza zwiększa miesiące
		keylock3=1;
		czas.MM++;
		if (czas.MM > 12)czas.MM=0;
	}else if(keylock3 && (PIND & (1<<KEY3)))keylock3=0;

	if (!keylock4 && !(PIND & (1<<KEY4))){ // // Naciśnięcie klawisza zwiększa dni
		keylock4=1;
		czas.YY++; // tu był problem
		if (czas.YY < 2000).czasYY=2000;
	}else if(keylock4 && (PIND & (1<<KEY4)))keylock4=0;

}

Problemem było zwiększanie się roku, bo po naciśnięciu klawisza zmieniała mi się 1  cyfra, czyli było click click, 2000,3000,4000. Doszedłem do wniosku, że chyba chodzi oto, że zmienna czas.YY jest uint16_t, a liczby w AVR są zapisane w formacie little endian, przez co najpierw zapisywany jest młodszy bajt, potem starszy i ja moją operacją zwiększałem ten starszy. Dopiero, gdy odwołałem się do tej zmiennej przez wskaźnik, do zadziałało jak należy. 

Dopiero od niedawna zacząłem trochę korzystać ze struktur i wskaźników, więc jest jeszcze pewnie niezrozumienie miejscami. Pytanie, czy dobrze kombinuje.

A tu funkcja, która działa jak chcę.

void display_menu2(Ttime *t){
	ustawiamy_date();

	if (!keylock2 && !(PIND & (1<<KEY2))){
		keylock2=1;
		czas.DD++;
		if (czas.DD > 31)czas.DD=0;
	}else if(keylock2 && (PIND & (1<<KEY2)))keylock2=0;

	if (!keylock3 && !(PIND & (1<<KEY3))){
		keylock3=1;
		czas.MM++;
		if (czas.MM > 12)czas.MM=0;
	}else if(keylock3 && (PIND & (1<<KEY3)))keylock3=0;

	if (!keylock4 && !(PIND & (1<<KEY4))){
		keylock4=1;
		t->YY++;
		if (t->YY < 2000)t->YY=2000;
	}else if(keylock4 && (PIND & (1<<KEY4)))keylock4=0;

}

 

Link do komentarza
Share on other sites

Jednak musiałem włączyć komputer 🙂

Chodziło mi o opisanie menu strukturą mniej-więcej następującą:

typedef void (*menu_func)(void);

typedef struct {
    const char *text;
    menu_func func;
}menu_t;

static const menu_t menu1[] = {
    { "RTC event(?)", RTC_Event },
    { "Ustaw date", display_menu2 },
    { "Ustaw czas", display_menu },
    { "Zapisz zmiany", zapisano_czas },
};	

Typ "menu_func" określa funkcje wywoływane po wybraniu opcji - na razie nic nie zwracamy, ani nie ma parametrów.

Następnie definiuję typ menu_t, który jest zwykłą parą <nazwa_opcji, funkcja>.

Teraz można po prostu utworzyć tablicę, której kolejne pozycje zawierają nazwy do wyświetlenia oraz funkcje do wywołania. Całość jest oznaczona jako "const", używamy więc pamięci flash do przechowywania całości, nie marnujemy RAM-u, ani tym bardziej nie dotykamy sterty.

To bardzo uproszczona pierwsza wersja, ale nie wymaga list, switch-y, maszyn stanowych ani innych super zaawansowanych cudów. Jeśli używamy C++ kod aż prosi się o użycie klas, ale ja na razie używam czystego C.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

20 minut temu, SOYER napisał:

@_LM_ co Ty n to co napisał @Elvis??

Mówisz o tablicowaniu funkcji? Uważam że to jest świetne rozwiązanie które często stosuję, nie tylko na potrzeby menu i nie tylko z funkcjami. Z resztą nieraz kłóciłem się o to z kol @ethanak Zawsze w większym zbiorze kolejnych parametrów lepiej je wrzucić do tablic. Swoją drogą z palca tego nie wyssałem: klik i klik

Edytowano przez _LM_
  • Lubię! 1
  • Pomogłeś! 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.