Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Kategoria: Kamery IP / Alarmy / Automatyka Bram
Montersi
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[AVR][C] - Menu wielopoziomowe

tehaceole 03 Paź 2012 07:31 14763 16
  • #1 03 Paź 2012 07:31
    tehaceole
    Poziom 28  

    Witam Kolegów
    Ostatnio zachciało mi się zmienić podejście do tworzenia menu. Do tej pory realizowałem je na konstrukcjach switch-case. Jednak ten sposób jest dość uciążliwy, jeżeli menu ma być mocno rozbudowane, ma posiadać submenu itp.
    Postanowiłem dokonać przesiadki na bardziej przyjazny sposób tworzenia menu. Odpowiedź była jasna: tablice, struktury i wskaźniki...
    Poszukałem, pogmerałem i znalazłem kilka bardzo ciekawych "modeli" menu. Były to m.in. menu Butterfly, tinymenu. Niestety jakoś nie mogłem się w nich odnaleźć. Czegoś mi w nich brakowało. Wreszcie w przepastnych czeluściach brzuszyska wujka Gugla trafiłem na projekt Niemca Tobiasa Schlegela. OLD_LCDmenu jest do obejrzenia tutaj a paczka z kodami jest do pobrania tutaj .
    Dodanie menu do własnego projektu nie sprawiło żadnych problemów. Brak warningów itp. tym bardziej napawał mnie optymizmem.

    Niestety za wcześnie się ucieszyłem. Po wgraniu aplikacji, która w pętli głównej nie robi nic więcej poza obsługą (nawigacja, wyświetlanie) menu przyszło rozczarowanie. Na głównym poziomie menu na wyświetlaczu znajdują się same "krzaczki". Dopiero po przejściu do submenu lub subsubmenu wszystko jest wyświetlane i obsługiwane "cacy". Wygląda mi to tak, jakby brakowało czegoś w inicjalizacji wskaźnika na główne menu. Poniżej kody:

    Kod: C
    Zaloguj się, aby zobaczyć kod

    Kod: C
    Zaloguj się, aby zobaczyć kod

    No i główna pętla programu:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Koledzy spójrzcie na ten kod i podpowiedzcie co może być nie tak, że wywołanie jako aktualnego menu *ui_TopMenu wywala same krzaki na wyświetlaczu, natomiast nawigacja po niższych poziomach menu przebiega bez najmniejszych problemów.
    Dodam tylko, że autor ma też dostępną nowszą wersję menu tutaj .

  • #3 03 Paź 2012 11:00
    tehaceole
    Poziom 28  

    Przyznam Ci Kolego, że to było drugie menu jakie brałem pod uwagę. Mógłbyś zamieścić jakąś fotkę lub filmik jak ono się sprawuje w boju? Jak wygląda możliwość budowy submenu dla danego menu?

    Do czego zmierzam? Szukam przyjaznego mechanizmu GENEROWANIA menu. Czemu generowania? Bo wiadomo - dla każdego urządzenia menu będzie inne. Jednak przy wspólnym mechanizmie generowania (nawigacja, wyświetlanie) menu całość sprowadza się do podmiany pozycji menu i zdarzeń do obsługi.

    W podanym przez Ciebie przykładzie poszczególne pozycje menu definiuje się w ten sposób:

    Kod: C
    Zaloguj się, aby zobaczyć kod

    Tak gapię się na ten obrazek z rozpiską stanów automatu i gapię i... Kurka to proste:) Tylko trzeba to sobie najpierw rozrysować na kartce a poźniej uważać żeby się nie zamotać przy wprowadzaniu do tabeli next_state.
    Nooo powiem Ci, że jest to zarąbiste i bardzo proste podejście, do tego wygląda niezbyt zasobożernie i przede wszystkim jest nie blokujące.
    Rewelka Kolego.
    Byłbyś tak uprzejmy i podrzucił tą swoją przerobioną wersję?

    Tak na marginesie podam przyczynę dla której szukam jakiegoś łatwego w implementacji "sposobu" na menu:[AVR][C] - Menu wielopoziomoweDodawanie kolejnych pozycji do tego i tak już rozrośniętego menu będzie po prostu nieporozumieniem... Menu pochodzi z tego urządzenia .

  • Pomocny post
    #4 03 Paź 2012 11:41
    excray
    Poziom 38  

    Niestety nie mam żadnego filmiku. Sprawuje się REWELACYJNIE. Za to podrzucę Ci gotowca bo ten na stronie wymaga sporo poprawek i domysłów co gdzie jest. Jest to wersja pierwotna która przechowuje w tablicy RAM. Funkcje których tutaj nie ma robią:
    LCDINIT - inicjalizacja wyświetlacza
    LCD_POS - ustawia pozycję: wiersz, kolumna
    LCD_CLS - czyści ekran
    LCD_PSTR - wyświetla ciąg znaków z FLASH
    LCD_DTA - wyświetla 1 znak.

    Kod: c
    Zaloguj się, aby zobaczyć kod

    A jak chcesz przerzucić tą tabelę do pamięci FLASH to poszukaj w moich tematach z [162] w tytule tam jest to opisane co i jak.

  • #6 03 Paź 2012 12:08
    tehaceole
    Poziom 28  

    tadzik85 napisał:
    Do opisu stanów warto użyć enuma.

    Kod stanie się czytelniejszy.
    To w 100% popieram. Po południa jak dorwę się do swojego urządzenia to przetestuję to rozwiązanie.

  • #7 03 Paź 2012 13:31
    tmf
    Moderator Mikrokontrolery Projektowanie

    Ponieważ wysłałeś mi info na PW, więc odpowiadam. Sprawę menu wyjaśniłem w swojej książce, a przykłady są dostępne za darmo dla wszystkich na ftp Helionu, tam też jest kompletny kod menu. Jeśli chcesz to go użyj, jeśli masz wątpliwości to chętnie pomogę i je wyjaśnię. Jak widzisz generalnie filozofii w tym wielkiej nie ma, po prostu struktura w formie listy zazwyczaj jednokierunkowej, z funkcjami obsługi poszczególnych pozycji w formie callbacków. Tu trudno wymyślić coś nowego i lepszego. Do opisu stanu wystarczy użyć wskaźnika wskazującego na aktualną pozycję menu.

  • #8 04 Paź 2012 07:43
    tehaceole
    Poziom 28  

    excray - pomęczyłem wczoraj te procedury. I jestem zachwycony!
    Plusy:
    - potrzebują mało zasobów
    Minusy:
    - trzeba uważać, żeby prawidłowo rozpisać wszystkie stany automatu

    Rozwiązanie zaproponowane w moim pierwszym poście (niestety nie padła odpowiedź czemu tak się ono zachowuje), drugie rozwiązanie tego Niemca oraz rozwiązanie zaproponowane przez Kolegę tmf są do siebie bliźniaczo podobne. W implementacji wydają się znacznie prostsze od tej maszyny stanów, jednak z drugiej strony potrzebują znacznie bardziej rozbudowanych funkcji do nawigacji po menu. Coś za coś. Na chwilę obecną zaproponowany przez Ciebie kod spełnia całkowicie moje oczekiwania. Znając życie niedługo sam dla siebie sprawdzę (dobrze opisane w książce) rozwiązanie tmf. Ale na chwilę obecną zostaję przy tym automacie. :)
    Poniżej kod tego co wczoraj spłodziłem:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Dokonałem zmian w funkcjach change_menu i display_menu. Przyjąłem koncepcję w której ekran wygląda następująco:
    [AVR][C] - Menu wielopoziomowe
    Zmiany w funkcjach miały na celu:
    - wprowadzenie migania edytowanej wartości,
    - eliminację zmiany wartości po wejsciu do menu edycji (previous_menu==current_menu).
    To tak na szybko:) Cała struktura menu już ładnie mi śmiga. Teraz linkuję odpowiednie funkcje do edycji poszczególnych wartości.

  • Pomocny post
    #9 04 Paź 2012 15:23
    tymon_x
    Poziom 30  

    Jak zobaczyłem tą tablicę, to aż ciary przeszły na plecach. Bardziej elegancko by wyglądała lista, modyfikować tablicę to jak koszmar z dzielnicy wiązów. Możesz zobaczyć technikę na zasadzie buttonów:
    Nieblokująca obsługa switchy - gotowiec ;-).

    I cała zabawa polega na wywołaniu:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    W menu najlepiej zrobić jako listę dwukierunkową góra/dół i lewo/prawo. To ma taką zaletę jak masz nowy ficzer w innym osobnym module, to wystarczy wywołać taką funkcję i masz nową pozycję w menu, a nie modyfikację mega tablicy i externowanie zasobów.

  • #10 04 Paź 2012 18:28
    tmf
    Moderator Mikrokontrolery Projektowanie

    tymon_x: Oczywiście rozwiązanie jakie pokazałeś jest doskonałe, ma tylko jedną wadę w świecie AVR - lista musi znaleźć się w SRAM (bo inaczej nie będzie można jej modyfikować). Jest to dosyć istotna wada, bo SRAM na AVR za wiele nie ma.

  • #11 04 Paź 2012 18:47
    tymon_x
    Poziom 30  

    tmf napisał:
    tymon_x: Oczywiście rozwiązanie jakie pokazałeś jest doskonałe, ma tylko jedną wadę w świecie AVR - lista musi znaleźć się w SRAM (bo inaczej nie będzie można jej modyfikować). Jest to dosyć istotna wada, bo SRAM na AVR za wiele nie ma.

    No tak, a jakby to obejść. Będzie to taki trochę high-level, stworzyć tymczasową strukturę w SRAM, przepisać ją do wolnego miejsca w FLASH (blank) na podstawie zarezerwowanego miejsca w linkerze, żeby się nie gryzło i usunąć z SRAM. W ramach pojedynczej inicjalizacji. Mam fantazję :)

  • #12 04 Paź 2012 19:26
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Sprawę można obejść prościej - wystarczy zrobić coś na kształt sekcji inicjalizacyjnych z newliba - po prostu w każdym module masz stałą umieszczoną w odpowiedniej sekcji i linker sobie już to wmontuje gdzie należy. Funkcja od menu po prostu zakłada że opis menu znajduje się w danej sekcji. Prostsze niż to co proponujesz [;

    4\/3!!

  • #13 04 Paź 2012 20:00
    tymon_x
    Poziom 30  

    Tylko, że dla każdego innego obiektu (innej struktury) byś musiał mieć inną sekcję, żeby wiedzieć co przeszukiwać, a mój pomysł polegał trzymaniu różnych obiektów, i elastycznym łączeniu w listę. Bo nie widzę jak wskaźniki prev i next w strukturach mają wiedzieć o innych strukturach, jeśli wszystko będzie pakowane w sekcje ? Chyba wiesz co mi chodzi (albo i nie) :)

  • #14 04 Paź 2012 20:22
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Przecież Twoja funkcja Button_add(Button_create(), pin_0, button_n, NULL); też niewiele wie o innych elementach...

    Jedyne czego by się wtedy nie dało zrobić to porządkowania listy - trzeba by więc dorzucić dodatkowy parametr definiujący kolejność. Oczywiście w przypadku zabawy z sekcjami inicjalizator miałby formę tablicy a nie listy.

    Zresztą - dyskusja jest bez sensu - jak brakuje RAM to trzeba wziąć coś większego [; Zresztą "za-externowanie" wszystkich nagłówków z elementami po to żeby w jednym pliku zbudować stałą tablicę/listę nie jest aż takim wielkim problemem przecież...

    W ARMach nie ma tego problemu [;

    4\/3!!

  • #15 08 Paź 2012 08:43
    tehaceole
    Poziom 28  

    Witam Kolegów
    Przepraszam, że przez kilka dni nie zabierałem głosu w temacie, ale bylo to niezależne ode mnie (awaria neta).
    A więc tak:
    - Po pierwsze chcę serdecznie podziękować Koledze excray - wdrożyłem przytoczone przez Ciebie rozwiązanie i jestem zachwycony. Oczywiście przeniosłem całe menu do pamięci programu. Stąd zmianie uległy procedury obsługi menu, które aktualnie wyglądają tak:

    Kod: C
    Zaloguj się, aby zobaczyć kod

    Zmodyfikowałem również strukturę opisująca daną pozycję menu. Dodałem pola definiujące zachowanie klawiszy (częstotliwość samopowtarzania):
    Kod: C
    Zaloguj się, aby zobaczyć kod

    I teraz dla przykładu menu o następującej strukturze:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Po rozpisaniu stanów autoamtu jako enumy:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Ma następującą postać po zakodowaniu w tablicy:
    Kod: C
    Zaloguj się, aby zobaczyć kod


    Teraz w pętli głównej wywołuję sobie wygodnie:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Gdzie funkcje generujące eventy z klawiszy wyglądają tak:
    Kod: C
    Zaloguj się, aby zobaczyć kod


    Koncepcja działania menu jest taka jak na rysunku z postu #8:
    - jezeli dana pozycja menu ma submenu to klikniecie ok powoduje wejście do submenu
    - jezeli dana pozycja posiada wartość do edycji to klikniecie ok na tej pozycji powoduje zaprzestanie wyświetlania w drugiej linii kolejnej pozycji menu, zamiast tego wyświetlana jest wartość edytowana.

    Ponieważ trochu tych wartości do zmiany miałem, musiałem napisać funkcje które umożliwią łatwą zmianę 1,2,3,4 wartości na jednym ekranie. Założenie było takie:
    - aktualnie edytowana wartość otoczona jest z obu stron zdefiniowanymi znakami (u mnie są to ">" i "<").
    - aktualnie edytowana wartość miga
    - pomiędzy wartości mogę wstawiać zdefiniowany znak (np ":" przy edycji czasu)
    - edytowana wartość moze byc wyswietlana z zadaną ilością miejsc po przecinku
    Na chwilę obecną wygląda to tak:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    i teraz wywoływanie tych potworków w konkretnych menu:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Jak widać wywołując te bloczki można od razu zdefiniować zachowanie klawisza next (u mnie down):
    - jeżeli w submenu jest tylko jedna wartość do zmiany to klawisz służy do jej dekrementacji
    - jeżeli submenu zawiera kilka wartości do zmiany to klawisz służy do przełączania się pomiędzy nimi

    Tak przy okazji to zmuszony byłem rozszerzyć swoje funkcje obsługi RTC PCF8583 o obsługę daty. Jak wiadomo zawsze w tym PCF są problemy z obsługą dnia tygodnia i roku. Ja zrealizowałem to tak:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Nie wrzucam tu kodów obsługi samego i2c ponieważ w większości pochodzą one z netu i są ogólnodostępne.
    Założenie było takie, że wartość roku (np. 12 dla 2012) przechowuję w pamięci RAM pcfa, natomiast zmianę roku (podczas normalnego odliczania czasu) dokonuję przez odczytywanie wartosci roku (0-3) z rejestru roku pcfa.
    Z kolei numer dnia tygodnia zrealizowany jest całkowicie z pominięciem pcfa - wykorzystałem tu algorytm wiecznego kalendarza Zellera.

    Od razu mówię: specjalnie co sekundę odczytuję z PCFa czas i datę - chcę eksperymentalnie stwierdzić jak będzie to wpływać na jego pracę.

    W wolnej chwili zrobię filmik prezentujący pracę tego menu.
    Jeszcze raz dziękuję Koledze excray.

    Teraz kilka słów do Kolegi tymon_x - przeanalizowałem Twoją funkcję obsługi klawiszy. Stwierdzić muszę, że jest cwanie napisana. To już bardzo wysoki poziom abstrakcji. O ile wydawało mi się, że rozumiem jej działanie o tyle wysiadłem przy zapisach:
    Kod: C
    Zaloguj się, aby zobaczyć kod

    Po jakiego czorta funkcje te zwracają liczbę pseudolosową?

  • Pomocny post
    #16 08 Paź 2012 12:11
    tymon_x
    Poziom 30  

    tehaceole napisał:
    Po jakiego czorta funkcje te zwracają liczbę pseudolosową?

    Post wyjaśniający

    tymon_x napisał:
    Kod pod PC często tak testuje i przenoszę do uC. Testowanie na mikrokontrolerze jest mało efektywne, zważywszy na brak technik typu Code Coverage czy Function Coverage. Albo ciekawsze testy jak symulacja zachowania EEPROM dla algorytmu emulowania EEPROM w Flash, jak błędy podczas kasowania, zanik zasilania i inne przyjemności. Tam był przykład generowania zakłóceń w sposób prostacki {0, 1};

    Jedyną jego rolą była symulacja napierniczania klawisza, tylko że można to odpalić po stronie PC (w tym wypadku Linux) i sprawdzić czy poprawienie działa debounce i zmieniają się stany przycisku ( low -> rising -> high -> falling -> low -> ... ) i co wypluwa do konsoli. I zwraca losowo 1 albo 0 :)

    To i tak niski poziom abstrakcji, lepiej to by wyglądało w C++ :)

  • #17 08 Paź 2012 12:50
    tehaceole
    Poziom 28  

    tymon_x napisał:
    I zwraca losowo 1 albo 0
    Tu akurat moje przejęzyczenie z tą liczbą psęudolosową. Dzięki za wyczerpujące wyjaśnienie. W sumie to ten sposób testowania nawet do głowy mi nie przyszedł... Ale cóż - język C nie jest w żaden sposób zależny od platformy. Wystarczy podmiana funkcji niskopoziomowych i już jakąś tam opracowaną przez siebie bibliotekę odpalasz na innym sprzęcie.

 
Promocja -20%
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
tme