Ads_700x200

poniedziałek, 27 lutego 2012

LCD HD44870 - WARSTWY - EFEKTY

Witam,

chciałbym zaprezentować dzisiaj wstępną prezentację mojego podejścia do obsługi standardowych i dobrze znanych wszystkim wyświetlaczy alfanumerycznych LCD opartych o sterownik HD44780. Każdy chyba zna te wyświetlacze, każdy wie już chyba o nich wszystko, jednak bywa, że często mamy wiele problemów z ich użytkowaniem we własnych programach w mikrokontrolerach. Oczywiście nie mówię tu o aplikacjach, które po prostu wyświetlają non stop na ekranie to samo np datę, godzinę i temperaturę, bo realizacja takiego celu jest tak banalna, że aż szkoda byłoby poświęcać czas na jej opisywanie. Za to często na forach internetowych ale także w mailach do mnie spotykam się z pytaniami typu: "Jak zrobić proste MENU" na wyświetlaczu alfanumerczynym LCD. Moim zdaniem...



.... w zdecydowanej większości ludzie, którzy zadają takie pytanie posiadają przede wszystkim problem nie z samym zorganizowaniem MENU ale z czymś innym co powoduje zawrót głowy przy podejściu do tego tematu. Wymieniłbym tu dwie główne przyczyny:

1. niestety poważne problemy z obsługą czy to klawiszy, czy pilota czy enkodera - czyli prościej mówiąc jakiegoś urządzenia wejściowego za pomocą którego można byłoby płynnie poruszać się po takim MENU

2. poważne problemy z właściwą organizacją programu tak aby nie był on pisany i aby nie działał w sposób liniowy ponieważ to rozkłada na łopatki większość podejść do każdego MENU. Czyli organizacja tych wszystkich funkcji do obsługi każdego z poziomów MENU

czy nie uważacie, że coś w tym jest ? Można się bowiem spotkać z tzw przepisami na MENU. Owszem niektóre są nawet bardzo fajne - jednak sprowadzają się one często do zapakowania całego projektu MENU w  jakieś sztywne ramy - powiedzmy struktur, które zorganizowane są w tzw listy jednokierunkowe czasem dwukierunkowe. O ile sama idea jest słuszna i to bardzo - to jednak rzadko można spotkać dokładny opis czy porady jak sobie z czymś takim radzić w praktyce, jak później tworzyć funkcje, które nie tylko będą wyświetlać na MENU na LCD - bo przecież to nie jest celem samym w sobie - ale jeszcze jak podłączyć kolejne własne funkcje, które już będą wykonywać jakieś rzeczywiste operacje w urządzeniu. Dodałbym więc tu jeszcze trzecią przyczynę:

3. brak umiejętności posługiwania się realizacją wielu procesów w mikrokontrolerze, które mogłyby działać w tym samym czasie. (A podstawy tego opisuję na łamach swojej pierwszej książki "Mikrokontrolery AVR Język C Podstawy programowania). Tyle, że często jak zauważyłem wiele osób pomija ten rozdział albo też próbę zmierzenia się z opisaną funkcją SuperDebounce() ... jak wielkiego kudłatego jeża uznając, że to tylko dla specjalistów....

W związku z powyższym chciałbym pokazać tutaj na razie pewną nową chyba ideę. Tylko proszę mnie źle nie zrozumieć, nie jest to żadne odkrycie! Takie sposoby są bowiem wykorzystywane już przez wielu programistów a także znanych nawet każdemu z nas programów - że się posłużę nazwą LCD Smartie!!!

Program ten pozwala w piękny sposób na wyświetlanie na takim alfanumerycznym LCD wielu danych z komputera PC za pomocą wielu dowolnych łącz. I nie ważne są tu na tym etapie rodzaje tych łącz, czy to RS232, czy USB czy LPT czy inne

Najważniejsze jest to żebyśmy spróbowali czasem podejrzeć to co inni robią już od dawna i jakie sposoby wykorzystują do tego celu. Właśnie o tym powiem teraz kilka słów.

Zasada działania takich programów opiera się na czymś takim jak WARSTWY (być może znane pojęcie tym osobom, które miewały do czynienia z programami graficznymi czy CADowskimi.) Zanim przejdę do krótkiego omówienia warstw to zwrócę uwagę na jedną bardzo ważną rzecz. Całe wyświetlanie opiera się na tym, że WSZYSTKIE ale to WSZYSTKIE operacje prezentujące na ekranie LCD jakiekolwiek dane nie odbywają się bezpośrednio na nim! Niemożliwe ? Jak to? zadasz może sobie pytania. Ano tak - zasada działania opiera się na tym, że fizyczny kontakt z LCD następuje w najprostszy z możliwych sposobów a jednocześnie najszybszy. Otóż co 20ms do 40ms na ekranie LCD wykonywane są tylko takie schematyczne instrukcje jak niżej i TO WSZYSTKO !!!!!! (zapamiętaj to)

lcd_locate(0,0);
lcd_str( buf_line1 );
lcd_locate(1,0);
lcd_str( buf_line2 );

to dla wyświetlacza 2 wierszowego , zatem łatwo sobie wyobrazić co będzie dla wyświetlacza 4 wierszowego:


lcd_locate(0,0);
lcd_str( buf_line1 );
lcd_locate(1,0);
lcd_str( buf_line2 );

lcd_locate(2,0);
lcd_str( buf_line3 );
lcd_locate(3,0);
lcd_str( buf_line4 );

przy czym buf_lineX zawiera tyle znaków ile kolumn posiada dany wyświetlacz LCD. Przyznasz, że takie operacje wziąwszy pod uwagę czas trwania tych operacji są najszybsze w dostępie do LCD. Nie jest tu broń Boże nigdzie używany rozkaz CLS !!!! .... I takie procedury są wykonywane jak wspominałem np co 20 ms. Widać z tego, że musi być jakiś BUFOR, który przechowuje każdą linię wyświetlacza gdzieś w pamięci RAM programu, który to realizuje. A skoro tak - to znaczy, że wszystkie pozostałe operacje powodujące, że cokolwiek ma się ukazać na wyświetlaczu LCD realizowane są właśnie na tym buforze. Poza tymi wyżej wymienionymi cyklicznie wykonywanymi procedurami NIGDZIE więcej w programie takim nie znajdziemy jakiejkolwiek innej funkcji operującej na LCD jeśli chodzi o wyświetlanie na nim czegokolwiek. (co najwyżej używane zostają czasem funkcje które pozwalają zdefiniować własne znaki użytkownika)

Dobrze skoro już wiemy jak wygląda podstawa takiego działania to już się orientujesz chyba, że te 20ms odpowiada nam za tzw czas odświeżania tego co dzieje się na ekranie LCD. A przecież 20ms a nawet 40ms to bardzo krótki czas. Można zatem wykonać mnóstwo różnych operacji na tym buforze obrazu LCD w pamięci RAM programu - aby wyświetlać przeróżne ciekawe rzeczy na LCD. Zachęcam cię do realizacji dostępu do LCD w ten sposób, nawet bez wykorzystania tego co opiszę dalej.

Kolejnym krokiem na który chciałbym zwrócić uwagę będą teraz działania na samym BUFORZE w pamięci RAM programu. Przede wszystkim czym one się charakteryzują i jakie mają zalety w porównaniu do bezpośredniego wyświetlania na LCD:

1. absolutnie szybszy czas dostępu do pamięci ekranu !
2. możliwość dokonywania zmian w takim buforze nawet z procedur obsługi przerwań !!!! co jest absolutnie nie zalecane a nawet zabraniane jeśli chodzi o wysyłanie fizycznych danych do LCD.
3. możliwość organizacji dużo większej ilości efektów specjalnych ale nie tylko - także dodatkowych buforów - odpowiadających za poszeczególne WARSTWY !!!

tak - na tym etapie dochodzimy do warstw. Bo skoro działamy na pamięci RAM to cóż za problem stworzyć sobie w pamięci RAM nawet tak niewielkich mikrokontrolerów jak AVR kilka buforów roboczych. Przecież jeden bufor dla typowego wyświetlacza LCD 2x16 zabierze nam zaledwie 32 bajty !!! to przecież niedużo. Zatem na takim mikrokontrolerze już jak ATmega88 możemy sobie pozwolić nawet na kilka takich buforów.

Kilka słów wyjaśnienie na temat warstw dla tych, którzy się nie zetknęli z czymś takim w programach graficznych. Otóż wyobraź sobie, że kładziesz przed sobą białą kartkę papieru - to będzie nasz główny BUFOR w pamięci RAM. Rysujemy sobie na niej nasz bufor w postaci 32 prostokątów ułożonych na kształt znaków w wyświetlaczu LCD albo 64 prostokątów jeśli mowa o wyświetlaczu 4x16 ;)

Na razie nie chcemy nic bazgrać w tym buforze - ale mamy ochotę aby pokazać w nim godzinę i datę w prawym górnym rogu. Bierzemy zatem przeźroczystą folię i kładziemy na białą kartkę przykrywając nasze prostokąty symbolizujące bufor 64 znaków wyświetlacza LCD 4x16 - będzie to nasza PIERWSZA WARSTWA. Na tej przeźroczystej folii piszemy w miejscach odpowiadająym znakom wyświetlacza w buforze pod folią cyfry i znaki - które pokazują nam godzinę i datę. Jeśli patrzeć od góry to z uwagi na to że narysowałeś to na folii przeźroczystej mamy efekt tak jakby te znaki były bezpośrednio narysowane na buforze prawda? Zatem bierzemy kolejną folię i także nakładamy ją na tą która już leży - to będzie nasza DRUGA WARSTWA. Tym razem na środku obszaru wyświetlacza rysujemy sobie tylko 8 znaków:

[alarm!]

Następnie bierzemy sobie kolejną folię przeźroczystą i rysujemy inne znaki - będzie to nasza TRZECIA WARSTWA. Przy czym nadal patrząc od góry - widzimy wyświetlacz, na którym widać razem:

1. godzinę i datę
2. napis [alarm!]
3. inny napis na trzeciej warstwie

właśnie ! - nasz program będzie miał za zadanie co 20 ms dokonywać połączenia w dół tych wszystkich warstw tak aby uzyskać jeden spójny obraz dla użytkownika. Posłużę się tu rysunkiem:

Myślę, że rysunek lepiej prezentuje to co starałem się tak żmudnie opisać, przy czym należy pamiętać, że każda warstwa to po prostu kolejny bufor w pamięci RAM. Jak się domyślasz - realizacja połączenia zawartości tych wszystkich buforów nie będzie chyba programowo jakąś trudną rzeczą prawda ? ;)

Co więcej, możemy tak zorganizować te bufory aby nie zajmowały one tyle znaków ile mieści się na całym ekranie danego LCD. Będziemy mogli tworzyć wręcz dowolne okienka. No bo jeśli np komunikaty mają się wyświetlacz w takim okienku jednowierszowym i ośmio-znakowym  zawsze - to po co nam większy bufor. Podobnie na trzeciej warstwie którą za chwilę użyję jako MONITOR dla pokazywania kodów z pilota jakie na bieżąco wciskam żeby wiedzieć jakie klawisze sobie przyporządkować do różnych operacji a chcę żeby zawsze wyświetlało się to na wierzchu ekranu i nie było zasłonięte przez inne warstwy ;)...

nawet więcej za chwilę na filmiku przedstawię przykład, gdzie tak na prawdę będzie użyta jeszcze kolejna warstwa do zrobienia MENU. Tymczasem spójrzmy jak można zorganizować te mniejsze bufory:


Każdy bufor będzie posiadał kilka swoich właściwości takich jak:

1. pozycja X
2. pozycja Y
3. szerokość - width
4. wysokość - height



dzięki temu tak na prawdę będę mógł dokonywać ciekawych operacji z tymi buforami. Wyobraź sobie na przykład, że gdy masz już równo ułożone te 3 przeźroczyste folie i wszystko ładnie wyświetla się na LCD, aż tu nagle łapiesz za środkową - WARSTWA 2 i zaczynasz ją powoli ciągnąć w lewą stronę !!! Co się powinno stać na LCD - zauważysz, że tylko ten napis na warstwie nr 2 zaczyna się przesuwać w LEWO ;) ... Cóż to dla nas za problem zrealizować to programowo skoro mamy do dyspozycji współrzędne warstwy takie jak X,Y oraz szerokość i wysokość ? Przy czym nasza cała biała kartka jest tak na prawdę wielkim VIRUALNYM buforem i w pewnym momencie gdy będziesz ciągnął tę folię z warstwy nr 2 - napis zacznie wychodzić poza obszar wyświetlacza trafiając na białą kartkę czyli nasz virtualny bufor. My natomiast programowo będziemy wyświetlać TYLKO TE ZNAKI z tej warstwy, które jeszcze znajdują się nad obszarem naszego fizycznego BUFORA w pamięci RAM ;) - to też proste zadanie.

Spróbuj teraz pomyśleć, jeśli do takich właściwości każdej warstwy dodamy jeszcze coś tak ciekawego jak własne zdarzenie EVENT, czyli prościej mówiąc procedurę która będzie wykonywana za każdym razem co 20ms gdy będą składane te wszystkie warstwy tuż przed wysłaniem ich na LCD - to nagle okaże się, że uzyskamy wręcz coś na kształt KLASY być może znanej ci z języka C++. No tak coś w tym jest - czuję się wręcz trochę teraz jak archeolog, gdy to piszę ponieważ jeśli ubierzemy taką warstwę w strukturę w języku C i dołączymy do niej wskaźnik do funkcji która ma być wykonywana wraz z jej obróbką - to nagle mamy do czynienia jakby z wykopaliskiem, które stanowi PROTOPLASTĘ mechanizmu, który w toku rozwoju (ewolucji) przekształcił się właśnie w klasy i rzeczywiste obiekty w języku C++. Myślę, że warto na takich przykładach pokazywać skąd się wywodzi język C++ i jak powstawał - żeby potem nie było przesądów i bajek wśród ludzi, którzy piszą na forach że twórcy języka C to sado-masochiści że wymyślili coś tak złego ludzkości - za to stwórcy języka C++ to całkiem inna ekipa aniołów, która stworzyła niezależnie i w oderwaniu od C język C++ ;) .... no może za daleko jak zwykle posunąłem się w swoich dywagacjach dlatego teraz wracam na ziemię i prezentuję jak to może wyglądać w tzw praniu - czyli w rzeczywistym działaniu na LCD. Myślę że to dopiero przemówi do każdego lepiej niż słowa i rysunki. A dodam, że to tylko wstęp . Bo jak pomyśleć że można będzie organizować warstwy z tak zwanymi obszarami zawierającymi właściwość TRANSPARENCY ;) żywcem jak z programów graficznych oraz praktycznym zastosowaniem, które opisuję własnie w książce, a do tego wszystkiego - próbę organizacji całości w jakieś zamknięte biblioteki, z których będzie można wygodnie korzystać - to myślę że tego typu sposoby na końcu przypadną wielu osobom do gustu. Oczywiście tym, które nie ulękną się nieco większej ilości linii kodu oraz posługiwania się wskaźnikami, które w tym przypadku stają się nieodzowne.

poniżej krótki film - taka mała zajawka możliwości rodzącej się biblioteki.



Na zakończenie powiem jedno, już kiedyś próbowałem na pewnym forum przedstawić takie podejście do obsługi LCD, jednak jak to bywa - zostałem zmieciony z powierzchni ponieważ kilku cwaniaczków wlazło, tłumacząc, że to bzdury i przerost formy nad treścią, że tak się nie robi z LCD, że to głupota i tym podobne

..... tymczasem ja powiem jedno, zwykle to co niezrozumiałe to wywołuje czasem na takich forach skrajne emocje. A ci fachowcy z koziej łaski mało mieli do czynienia z takimi zagadnieniami (mam tu na myśli tych, którzy psioczą na tego typu sposoby obsługi LCD. Na ten temat też wybuchła swego czasu mini wojna na jednym z forów). Wiem za to z kursów które mam przyjemność prowadzić, że każdy kto spotkał się z takimi rozwiązaniami jest później nimi tak samo zafascynowany jak ja i zaczyna ich z chęcią używać na co dzień. A przypominam, że to nie jest żaden mój wynalazek czy odkrycie - to jest raczej próba przybliżenia tego co używają już od dawna mądrzejsi od nas w swoich fajnych i zaawansowanych programach, które podziwiamy na co dzień ale nie zawsze zdajemy sobie sprawę:

"JAK TO JEST ZROBIONE?"

;)

..... a na zakończenie dla tych, którzy myślą, że tu chodzi o jakieś tam tylko durne pseudo animacje na LCD, jeszcze jeden przykład, z którego mam nadzieję jeszcze lepiej widać że to będzie kolejny przykład tworzenia systemu wielozadaniowego (pseudo-wielowątkowości) na AVR .... tyle że tym razem każdy z niezależnych procesów dostaje do dyspozycji swój własny bufor pamięci RAM - pamięci EKRANU dzięki czemu każdy z procesów może uwaga!!! NIEZALEŻNIE wykonywać swoje operacje bądź to na całym ekranie , bądź na jego dowolnym fragmencie. Ale uwaga - każdy z procesów może także wykonywać swoje zadania w tle bez żadnego kontaktu z ekranem. Tak jak w jednym komentarzu na dole dodam - że możliwości uzyskania efektów specjalnych jak animacje są tutaj niejako efektem ubocznym - ot tak ! można je mieć przy okazji za DARMO ;) przy takim podejściu do programowania. Jeśli więc ktoś postrzega to tylko jako animację na LCD - to rzeczywiście - dalej nie ma co czytać opracowania tego tematu, które się wkrótce ukaże.

poniżej kolejny filmik - prezentujący jeszcze dodatkową warstwę z użyciem efektu przeźroczystości i nie jest to tylko gadżet - można bowiem - jeśli ma się wyobraźnię bardzo fajnie zorganizować wszystko co się przyśni na dowolnym LCD alfanumerycznym ;)..... Dodatkowo dodane zostało teraz maleńkie zdarzenie do warstwy monitora kodów z pilota, dzięki czemu kody pojawiają się tylko na chwilkę tzn na np 2 sekundy i znika warstwa. Zwróćcie uwagę proszę na to, że to co widać na LCD to efekt działania wielu procesów i każdego z osobna a nie jakaś jedna mistyfikacja - animacja ;)


w zasadzie to pętla główna tego hmmm niby skomplikowanego programu wygląda tylko tak ;)
i to wszystko - reszta obsługiwana jest pięknie w oddzielnych zdarzeniach .



Przy okazji na zakończenie podam przykład do tego do jakich granic może posunąć się ignorancja jeśli chodzi o wykorzystywanie takich prostych mechanizmów programowania. Toż niektórzy potrafią polecać/sugerować FreeRTOS nawet do tego żeby wykonywać dwie niezależne funkcje na 8-bitowcu. A skoro tak, to w zasadzie nie ma co używać 8-bitowca - bo nie da rady - lepiej od razu postawić ARM a na nim FreeRTOS'a albo linuxa i proszę mamy multitasking. Bo przecież na 8-bitowcu to herezja. A ja uważam, że właśnie takie podpowiedzi to herezja i są wręcz szkodliwe - oto przykład:

http://www.elektroda.pl/rtvforum/topic2239451.html

64 komentarze:

  1. W 43 sekundzie usłyszałem słowo 'książka'
    Mógłby Pan trochę przybliżyć miesiąc wydania tejże książki?

    P.S obsługa warstw mi się podoba..
    Ale niestety muszę tu się z niektórymi zgodzić...
    Również wydaje mi się to przerostem formy nad treścią..
    Ale z gotowej biblioteki chętnie bym skorzystał :]

    OdpowiedzUsuń
  2. No ja już przywykłem do tego, że ludzie nazywają przerostem formy nad treścią pewne techniki programowania. I nawet nie protestuję ani nie chcę na siłę uszczęśliwiać nikogo ;) to po pierwsze

    Po drugie mało kto z początkujących zadaje sobie trud programowania chociażby w oparciu o tzw SYSTICK, za to z uciechą przesiada się z 8-bitowca na ARM bo to bardziej modne i nadal zamiast programować męczy biednego procka. A jak nic nie wychodzi to co ? to szuka jak odpalić jakiegoś linuxa na ARM, żeby mieć jakiś gotowy system pod ręką dzięki czemu pisanie prostych programów (nie przesadzę jeśli powiem że takich co to migają diodą LED) sprawia mu mniej trudności bo system zadba o dostarczenie prostszych gotowych funkcji API czy też jakiegoś Frameworka. No tak - bo przecież wykorzystanie takich mechanizmów na 8-bitowcu to HARDCORE.

    O ile rozumiem takich początkujących co to zaczynają od BASCOMA - albo ARDUINO chcąc się posłużyć gotowymi rozwiązaniami do realizacji swoich prostych amatorskich celów to niestety tych, którzy rzucają się na coraz to większą architekturę nie mając pojęcia o programowaniu to już mi szkoda i przykro patrzeć.

    Tylko niech mnie kolega źle nie zrozumie, ja tu nie piję do kolegi. To mniej więcej tak samo jak z tak prostym tematem jak parsowanie danych przy komunikacji asynchronicznej dowolnego typu np przez RS232, sprawa jest prosta a nawet banalna gdy się zorganizuje to w oparciu o zdarzenia czy tzw timery programowe. No ale wiadomo - tu też można napisać że to już HARDCORE ;)

    Patrzę na to z uśmiechem ale nie jest on ani troszkę ironiczny, szkoda mi tylko, że hmm no troszkę takie wypowiedzi pokazują jakby nie patrzeć pewną ignorancję niestety. Bo zamiast szukać istoty i sedna sprawy - dlaczego tak jest, po co ktoś stosuje takie rozwiązania - to łatwiej uznać je za zbyt trudne i się poddać - dlatego mi szkoda właśnie.

    OdpowiedzUsuń
  3. [....i druga część wypowiedzi....]



    Ale bardzo się cieszę z takiej opinii, którą tu kolega przytoczył że to "przerost formy nad treścią" bo widzę, że nawet nie próbując zbliżyć się do sposobu działania czegoś takiego i dopiero wtedy oceny czy to trudne czy łatwe - lepiej od razu uznać, że to zbyteczne - jakieś tam pseudo animiacje na LCD ;) .... a szkoda ... bo animacje czyli bajeranckie efekty że tak powiem to można uzyskać tu niejako przy okazji - wręcz jako odpad - przy realizacji najważniejszych celów - do których można zaliczyć programowanie współbieżne - to po prostu kolejny przykład. Ale po co ? ;)

    Myślę w związku z tym - i też bez żadnej ironii że przy takim podejściu jednak druga część książki raczej kolegi na pewno nie zainteresuje - tu będzie sporo takich nudnych i mało zrozumiałych przykładów...

    Jakiś czas temu, ktoś tu na moim blogu podał przykład takiego fajnego skąd innąd języka PASCAL dla AVR pewnej niemieckiej firmy. Pisałem, że jest to świetne rozwiązanie tyle że zamknięte i bardzo drogie. Jednak 3-4 lata temu gdy sam szukałem czegoś podobnego jeszcze nie do końca rozumiejąc język C - bo strasznie trudno było mi się przestawić z programowania w Pascalu na PC od 20 lat to sam pobrałem sobie wersję DEMO tego pakietu.... A że Pascala znam jak własną kieszeń to może z tej racji łatwiej było mi dojść co ta firma ma w zanadrzu dla programistów posługujących się ich środowiskiem. I jak to Niemcy - przygotowali super solidny produkt a ich KRETORY szablonów pustych programów były tak świetne, że nie umywają się do nich np te z płatnych pakietów dla AVR do języka C jak np Codevision. Szybko zacząłem się im przyglądać i to dzięki zawzięciu i ich rozgryzaniu doszedłem do tego jak prosto można sobie organizować pracę każdego programu na AVR. I staram się to teraz ludziom przekazać. Stąd końcówka mojej pierwszej książki poświęcona jest właśnie takim rozwiązaniom - bo na 8-bitowcach też można - tylko trzeba chceć i nie jest to trudne. A że mam ten komfort, bo dzięki książce mam także kontakt z dużą ilością czytelników to jednak wiem, że jest także wielu, którzy nie tylko docenili moje starania - ale co ważniejsze - już dzisiaj z nich korzystają na co dzień - i nie narzekają, że to jest za trudne itp ...

    Tyle mogę koledze odpowiedzieć.

    OdpowiedzUsuń
    Odpowiedzi
    1. BOŻE, gdzie byłeś jak kupowałem książkę "Język C dla ...avr", 50 zł wydane w 500 bezwartościowych kartek [dobrze że kupiłem na allegro za 1/2 ceny]... mimo że to nie temat o tym, to i tak chce się wyżalić... czyta się ją z podekscytowaniem jak książkę "Ewidencja VAT - zasady księgowości"
      Pamiętam jak zawsze rozbierałem wszystkie elektryczno-mechaniczne przedmioty w domu od zabawek po monitory CRT, zastanawiałem się nad ich działaniem… Pewnego czasu parę lat później wszedłem na sklep-avt znając już nazwę [tego małego czarnego „klocka”] AVR. Niestety jak zobaczyłem cene programatora-700zł moja ekscytacja dobiegła końca (chwilowo).
      Książkę pana Borkowskiego wybrałem z powodu, iż pomimo podziału książki na 2 architektury [avr/arm] posiadała tajemniczy język jakim jest 'micro-PASCAL'. Jako że język ten darzę ogromnym sentymentem bo to mój pierwszy język programowania PC jeszcze z czasów gimnazjalnych i w sumie władanie nim opanowałem bardzo dobrze więc po instalacji tego IDE do mikro Pascala i zacząłem pisać proste programiki na ATmege 8 … i wtedy olśniła mnie myśl o zrobienie mechanicznego ramienia o kilku stopniach swobody. Konstrukcja miała być oparta na serwomechanizmy.
      No i tu pojawił się problem bo okazało się że do obsługi potencjometru potrzebne jest sterowanie przetwornikiem ADC… jako że informacji o tym nigdzie nie mogłem znaleźć a po napisaniu na elektrodzie zostałem wyśmiany, to musiałem poznać nowy język-C. Nie jest taki straszny (choć daleko mu do przejrzystości Pascala :) ). Wracając do tematu to strasznie się zawiodłem na książce p.Francuza, Panie Mirku… jest Pan genialny… szkoda że na początku nie zakupiłem pana książki lecz myślałem że to będzie jakiś nudny podręcznik od ATNELA… Jaka ta pana książka jest? jeszcze nie wiem ale myśle że na pewno się nie zawiodę i z pewnością ją kupie!!!(mimo sporej ceny).
      Co do pokazu informacji na LCD podzielonej na warstwy to mogę powiedzieć tylko że to świetny pomysł i postaram się z niego skorzystać w moim nowym projekcie termostatu z alarmem
      Serdecznie Pozdrawiam i życzę żeby się panu chciało pisać takie świetne poradniki na pańskim blogu jak dotychczas!!! :)
      Rafał

      Usuń
    2. Też mam nadzieję, że książka się przyda ... a na pewno będzie dobrą bazą do poradników video z mojego bloga i youtube ....

      Usuń
  4. Z zainteresowaniem przeczytałem "zajawkę" Kolegi Mirka odnośnie stosowania buforowania zapisu do LCD alfanumerycznego. Skąd wzięło się moje zainteresowanie?
    Już tłumaczę. Często zdarza się, że po dłuższej pracy urządzenia pod wpływem zakłóceń zewnętrznych na wyświetlaczu pokazują się "duszki", jakieś znaki w przypadkowym miejscu.
    Wyświetlacze zrealizowane na omawianym przez Mirka sterowniku charakteryzują się tym, że po wyświetleniu danego znaku jest on "zatrzaskiwany" na LCD aż do jego nadpisania innym znakiem.
    Stąd często zachodzi konieczność nadmiarowego używania spacji do nadpisywania miejsc, które wg założeń powinny pozostać puste na wyświetlaczu. Istnieje także inny sposób, który omówię później wraz z jego wadami. Zatem zacznijmy nasze rozważania:
    1)
    ustawiam kursor w interesującej mnie pozycji:
    LCDxy(0,0); //linia pierwsza, znak pierwszy
    Wypisuję na wyświetlacz tekst:
    LCDtext("hello LCD");
    Tekst ma długość 9 znaków. I wszystko gra :) Mamy nasze powitanie na wyświetlaczu. Tylko co się stanie w przypadku pojawienia się "duszka" na pozycji np 13 ? Jeżeli tak się złoży, że w programie nie używam pozycji 9-16, to taki "duszek znikąd" będzie sobie siedzieć na wyświetlaczu tak długo, aż nie użyję komendy LCDcls();. Można ten problem wyeliminować stosując dopełnienie spacjami:
    LCDtext("hello LCD ");
    Tylko czy takie podejście ma jakikolwiek sens jeżeli w programie używamy kilkudziesięciu komunikatów? Te "czyszczące duszki" spacje to niesamowite marnotrawienie pamięci mikrokontrolera :(
    2)
    Nieco inaczej sprawa wygląda, gdy w pętli głównej cały czas wyświetlam sobie jakiś napis:
    LCDxy(0,0);
    LCDtext("hello LCD");
    A przy użyciu np. timera programowego co 100 lub 200ms wywołuję funkcję LCDcls();

    //cykliczne czyszczenie wyswietlacza - unikniecie smieci po dlugiej pracy
    if(!L_czyscLCD) //jezeli timer programowy L_czyscLCD zliczyl do zera to:
    {
    L_czyscLCD=10; //zaladuj timer nową wartością 100ms (10x10ms)
    LCDcls(); //czysc LCD
    }

    I wszystko niby gra :) Na ekranie nie ma już żadnych śmieci, co więcej: teraz można już dynamicznie zmieniać teksty na wyświetlaczu:
    LCDtext("nowy");
    spowoduje wyswietlenie tekstu "nowy" :) W poprzednim przypadku wyswietlony zostałby "nowyo LCD". No chyba, że wyświetlilibyśmy coś takiego:
    LCDtext("nowy ");
    czyli nadpisalibyśmy wcześniejszy tekst spacjami.
    I niby już wszystko gra :) Ten sposób testowałem na wielu różnych wyświetlaczach i spisywał się znakomicie. Ma on jednak jedną istotną wadę:
    wszystko jest ok dopóki patrzymy na wyświetlacz "na wprost". Wystarczy, że zmieni się kąt patrzenia np na 60 stopni... I co? I wtedy widać już jak na dłoni, że wyświetlacz jest cyklicznie czyszczony :(
    I masz babo placek... Niby nic wielkiego, ale niektórym użytkownikom naszego urządzenia coś takiego może się nie spodobać. Pytanie: jak temu zaradzić? Tutaj właśnie odpowiedzią jest buforowanie danych do wyświetlenia w pamięci RAM mikrokontrolera i wyświetlanie dopiero tak przygotowanych danych :)

    OdpowiedzUsuń
  5. 3)
    Mirek przygotowuje zaawansowaną bibliotekę, która z punktu widzenia użytkownika będzie "przezroczysta". Oznacza to, że funkcjami wyświetlającymi informacje będziemy posługiwać się tak jak wcześniej robiliśmy to z funkcjami bezpośrednio zapisującymi do LCD. Subtelna różnica polega na tym, że zapis do LCD odbywać się będzie "w tle" - my cały czas operujemy tylko na pamięci RAM.
    Jak to wygląda w praktyce? Ano załóżmy, że na pozycji 1 linii pierwszej chcemy mieć na stałe wyświetlony jakiś komunikat. Zaś na pozycji np. 12 chcemy wyświetlać komunikat dynamiczny, mogący np. migać. Przy wykorzystaniu 2 poprzednich sposobów da się to osiągnąć, jednak będzie to:
    a) pamięciożerne (nadpisywanie spacjami)
    b) powodować może migotanie całego ekranu

    Sposób Mirka pozwala zrealizować takie "animacje" bez najmniejszego problemu :) W ramach ćwiczeń napisałem naprędce do bólu prosty i nieefektywny kod:
    char linia1 [17];
    char linia2 [17];
    bool zmien;
    void text(char *txt,uint8_t nr,uint8_t start) {
    while (*txt) {
    if(start<=17)
    {
    if(nr==1)
    linia1[start]=*txt;
    if(nr==2)
    linia2[start]=*txt;
    start++;
    }
    txt++;
    }
    return;
    }
    Kod absolutnie nie jest zoptymalizowany ani zbytnio zabezpieczony przed przekroczeniem zakresu tablicy, miał jedynie pokazać ideę wyświetlania buforowanego.
    w głównej pętli programu na stałe wywoływane jest coś takiego:
    LCDxy(0,0);
    LCDtext(linia1);
    LCDxy(0,1);
    LCDtext(linia2);

    i teraz z użyciem timera programowego powoduję miganie tekstu:
    if(!L_LCD)
    {
    L_LCD=10;
    zmien=!zmien;
    }
    if(zmien)
    {
    text("ja",1,0);
    text(" ",2,0);
    }
    if(!zmien)
    {
    text("ty",2,0);
    text(" ",1,0);
    }

    OdpowiedzUsuń
  6. Przy czym dalsza część komunikatów wyświetlanych na LCD nie ulega jakimkolwiek zmianom, przekłamaniom itp. Nie występuje także efekt "duszków" :)
    Rzecz jasna użyłem tutaj spacji do oczyszczania ekranu, jednak już we właściwej bibliotece obsługi będą do tego użyte pętle powodujące, że cała zawartość bufora różna od tej którą do niego zapisujemy będzie czyszczona. Podany powyżej przepis pozwolił mi na wspaniałe uzyskanie efektu migania fragmentu tekstu z jednoczesnym brakiem problemów z tekstem wyświetlanym "statycznie".


    IMHO sposób Mirka jest nie tyle co wart uwagi - jest on wręcz niezbędny w zaawansowanych aplikacjach. Należy tu jednak mieć na uwadze przesiadkę ze sposobu liniowego programowania na programowanie "pseudo wielowątkowe". Taki tryb programowania stosowany jest powszechnie we wszelkiej maści sterownikach PLC. Przykładem niech będzie funkcja opóźnionego załączenia: w programowaniu liniowym funkcja taka na dłuzszy czas zablokowałaby przetwarzanie reszty programu. W sterowniku PLC deklaruję gotowy blok takiej funkcji, inicjuję ją sygnałem wejściowym a reszta mnie nie interesuje. Program się przetwarza a po jakimś czasie otrzymuję na wyjściu bloku sygnał o zakończeniu odmierzania czasu. W mikrokontrolerze niestety musimy samodzielnie opracować tego typu funkcję (jak zresztą wszystkie inne funkcje działające "wątkowo"). Zatem potrzebna jest zmiana przyzwyczajeń programistycznych, niektórych nawyków. Przede wszystkim potrzebna jest permanentna zmiana podejścia do "ciała" programu. W efekcie możliwe są do uzyskania zarówno efekty prezentowane w książce Mirka (rozdział "wprowadzenie do systemów czasu rzeczywistego") jak i wiele innych, znacznie bardziej zaawansowanych.
    Mam nadzieję, że moje przydługie opracowanie będzie zachętą dla wielu malkontentów nazywających takie sposoby "przerostem formy nad treścią"... Takim przerostem jest propozycja zastosowania FreeRTOSa dla przysłowiowego "mrygania diodą": http://www.elektroda.pl/rtvforum/viewtopic.php?p=10634041#10634041 Opisywane w tej dyskusji zagadnienie jest tak banalnie proste do wykonania, że aż głupio tutaj nawet wspominać o RTOS :) Jednak wielu programistów przyzwyczajonych do programowania liniowego nie widzi niestety innego wyjścia z sytuacji...

    Konkludując: programowanie "wątkowe" jest znacznie trudniejsze od liniowego. Jednak warto po nie sięgnąć ze względu na korzyści jakie daje.

    Pozdrawiam
    TS

    OdpowiedzUsuń
  7. Witam, bardzo chętnie śledzę wątki które się na tej stronie pojawiają i jestem pod wrażeniem tempa rozwoju.

    Smutne jest jednak to że w większości opis jest poświęcony ludziom którzy pomysły Mirka i ich realizację negują.

    "Przerost formy nad treścią" - czy te osoby wiedzą co to znaczy?! Odnosząc się do tego konkretnego przykładu, im atrakcyjniejsze wyświetlanie i ciekawe animacje tym lepiej dla projektu a także dla integracji urządzenia z użytkownikiem. Na dodatek jeśli taka biblioteka do animacji i rodzaju "wielowątkowego wyświetlania" nie będzie zajmować przesadnie dużo pamięci kontrolera to nie można tu mówić o przeroście formy nad treścią ale o jeszcze pełniejszym wykorzystaniu jego możliwości i wiedzy z zakresu programowania.

    Im popularniejsze mikrokontrolery tym więcej prac z przysłowiowym "mikrokontrolerem do zapalania diody", niejednokrotnie autorzy z niewielka wiedzą po kilku projektach myślą że są już "bogami" w temacie i wypisują bzdury. Przecież jeśli akurat ktoś nie korzysta z animacji bo ich nie lubi albo w jakiś sposób nie pasują mu do projektu to nie musi takiej biblioteki dołączać, nie ma to jednak nic wspólnego z przerostem formy nad treścią!

    Mirku nie daj się zdemotywować takimi bezmyślnymi oskarżeniami. Masz bardzo dobry styl pisania programów i coraz ciekawsze i głębsze zagadnienia realizujesz. Zyskałeś wielu fanów i obserwatorów którzy z cała pewnością będą cię wspierać. Ponadto myślę że nie tylko ja dziękuję Ci za to że dzielisz się tą wiedzą na forum.

    Pozdrawiam DP

    OdpowiedzUsuń
  8. Bardzo dziękuję za słowa otuchy, czasem są one bardzo potrzebne.... Tymczasem, myślę że w swojej kolejnej książce udowodnię, że takie podejście jak tu prezentowane, nie musi być trudne w kodzie programu a przydać się może wcale nie tylko do animacji....

    Bo o animacji wspominam jako o fajnym wręcz efekcie ubocznym, który można wykorzystać lub nie (wg woli) .... ciekawsze są o wiele bardziej programowe metody realizacji takich programów - bo one pozwalają pokazać tak w naoczny sposób a nie czysto abstrakcyjny i teoretyczny - jak to może działać w praktyce.

    OdpowiedzUsuń
  9. mirekk36 Fajny pomysł z tym buforowaniem na pewno niejeden niedowiarek będzie z tego korzystał! ;) a tak nawiasem mówiąc jak już jesteśmy przy wyświetlaczach LCD korzystam z twojej książki i brakowało mi takiej funkcji jak lcd_uint() dzisiaj spędziłem trochę czasu szukając błędu w programie a okazało sie ze lcd_int() nie wyświetli poprawnie zmiennej uint16.
    A jeśli chodzi o następna książkę to mile widziany by był projekt komputerka do auta z wszystkimi bajerami(serwis,km,prędkość,temp itp). Sam nad takim myślę ale niestety jeszcze nie posiadam takiej wiedzy aby tego dokonać.
    Pozdrawiam.

    OdpowiedzUsuń
    Odpowiedzi
    1. No to taka uwaga do tego "nawiasem" ;) W programie nie ma żadnego błędu, a to że funkcja lcd_int nie potrafi wyświetlić pełnego zakresu uint16_t to nie jest żaden błąd panie kolego - trzeba zajrzeć sobie do jej wnętrza. Ona tak została napisana żeby wyświetlać liczby z zakresu -32768 do +32767. Zatem zakres uint16_t od 0 do 32767 się wyświetla ;) .... A jak by ktoś chciał więcej - to przecież w książce wyraźnie piszę, że:

      1. funkcja nazywa się lcd_int - co sugeruje że będzie wyświetlać właśnie liczby ze znakiem a nie bez znaku.

      2. jeśli by ktoś chciał sobie przerobić ją tak żeby wyświetlała pełne uint16_t to co za problem ? Wystarczy wewnątrz niej zamienić funkcję ITOA() na LTOA() .... ale wtedy mógłbyś sobie stworzyć i nadać np nową nazwę swojej funkcji: lcd_uint()

      Po prostu trzeba poczytać w książce jak się te biblioteki tworzy - bo opisując to wcale nie miałem na myśli żeby kogoś na siłę uczyć konkretnie zbudowania biblioteki do LCD. Moim celem była nauka technik programowania w C na przykładzie tworzenia takiej biblioteki. I dlatego jeśli ktoś sobie pozwoli pominąć w czytaniu taki czy inny rozdział (bo uważa że jest jakaś tam gotowa biblioteka na DVD) to ma ZONK'a ;) .... bo właśnie w tekście jest opisane co i jak a także jakie było założenie i sposób działania funkcji lcd_int(). Jak widzisz więc - to nie żaden BŁĄD

      No ale szerzej już komuś opisywałem to na naszym forum:

      www.forum.atnel.pl

      i tam też cię zapraszam żeby doczytać o tym więcej, bo nie ty pierwszy i pewnie nie ostatni - myślisz że w tym lcd_int() jest jakiś błąd. Tymczasem warto od podszewki poznać jak ta funkcja działa - bo wtedy będzie wiadomo jak ją sobie dostosować dalej - OK ? ;)

      Usuń
  10. Ale Panie Mirosławie ja nigdzie nie napisałem ze jest gdzieś błąd w lcd_int() po prostu potraktowałem lcd_int() jako funkcje wyświetlania zmiennych w tym tez uint dla tego szukałem błędu w programie swoim a nie w bibliotece LCD ale gdy oświeciło mnie gdzie jest błąd od razu zaglądnąłem do stdlib i zastąpiłem ITOA() na UTOA() pan proponuje na LTOA() a to czasem nie jest long integer to string ? Tak czy siak z UTOA() działa prawidłowo ;) Dla tego napomniałem ze może warto by było napisać funkcje dla zmienej usigned i w rzucić to w plik z poprawka do LCD (który już istnieje na pana stronie ) na pewno możne się to komuś przydać ;) Przy pisaniu programu dużo używam Ctrl + Space wiec jak by mi się wyświetliło w podpowiedzi lcd_int() i lcd_uint() na pewno nawet odruchowo bym wybrał lcd_uint() a tak to mnie coś "zaślepiło" :P no ale w końcu dopiero nabieram wprawy wiec mój błąd jest wybaczalny ;)

    OdpowiedzUsuń
    Odpowiedzi
    1. Ależ oczywiście że ja nie mam nic do kolegi ;) po prostu i przy tej okazji tłumaczę co i jak - bo tak jak pisałem już nie jedna osoba podobnie podeszła do tej funkcji lcd_int(). Dlatego ja się bardzo cieszę, że kolega sam dotarł do funkcji, dzięki którym może wyświetlić także uint16_t. I tak na prawdę to jest głównym celem tej książki. Nie gotowa biblioteka z milionem funkcji, z których nie każdy będzie korzystał. Ale przepis na bibliotekę z jej początkiem .... a dalej każdy może już ją rozwinąć w swoim kierunku bo teraz już wie jak - nieprawdaż ???? ;)

      I dlatego nie będzie akurat w tym względzie jakichś poprawek do kodu. Takie się pojawiają, jeśli rzeczywiście znajdzie się jakiś BŁĄD, tak jak to miało miejsce przy inicjalizacji - stąd moja aktualizacja. Natomiast można by dopisać jeszcze wiele użytecznych funkcji do tej biblioteki - tyle że to już pozostawiam szanownym czytelnikom. Taka zresztą była moja idea od samego początku gdy zabierałem się za pisanie tej książki.....

      ..... dopiero w kolejnej (druga część), która się niedługo ukaże, będzie nieco inaczej - tzn, ukażą się przepisy na różne fajne biblioteki, ale niektóre z nich będą na tyle kompletne, że nawet bez wgłębiania się w szczegóły, jeśli ktoś będzie chciał - to od razu gotowe do użycia ;)

      Usuń
  11. Witam,
    może niezupełnie na temat, ale na pewno blisko: używa Pan linii RW? Mógłby Pan napisać ile zajmuje zapisanie wyświetlacza (ew jednej linii)? A może lcd po jednym znaku jest zapisywane? Mam gotową płytką na której RW zwarte jest do masy i zastanawiam się, czy nie warto by jej "zdrutować".
    Pozdrawiam.

    OdpowiedzUsuń
    Odpowiedzi
    1. Ja ZAWSZE używam linii RW podłączonej do procka. No może prawie zawsze tzn w 99% moich projektów. Dlaczego? No właśnie dobre pytanie zadałeś. Pytasz o czasy... ale przecież one jak coś (przypominam tylko) podane są w notach PDF sterowników hd44780 i dla POJEDYNCZEGI ZNAKU wynoszą całą wieczność z punktu widzenia procka bo aż 120us !!! a mowa to tylko o wysyłaniu jednego znaku a nie np operacji czyszczenia ekranu CLS !!! gdzie już mamy do czynienia z czasami podawanymi w milisekundach! ... to wtedy gdy RW jest podpięte do GND.

      Za to gdy RW jest podłączone do procka to czas znacznie się skraca ? Ile konkretnie ? teraz nie odpowiem - ale po prostu badamy programowo odpowiedź z wyświetlacza, kiedy wykonał już operację - to musi być dużo szybciej ;) .....

      Widać to idealnie podczas tworzenia byle animacji czy przesuwaniu tekstów na wyświetlaczach. Tzn te różnice.

      A jeśli chodzi o realizację takiego projektu jak tu opisuję to wręcz można zapomnieć o dobrych efektach przy RW zwartym do GND.

      Dlatego jak masz możliwość to ZAWSZE wykorzystuj RW i sprawdzanie BusyFlag ;) Polecam.

      Usuń
    2. "wręcz można zapomnieć o dobrych efektach przy RW zwartym do GND." Czy to ma szansę dobrze działać na TWI?

      Usuń
  12. I ja dołożę małą sugestie, warstwy to fajna i chyba normalna sprawa, kto kiedyś programował komputery 8bit czy nawet większe jak Amigi wie że bez tego ani rusz, ekran jest tylko do prezentowania danych, a całe operacje na danych powinny być ukryte zaszyte:) Ja bym dołożył jeszcze taki buforek z aktualna zawartością wyświetlacza, wtedy podczas zmian na wyświetlaczu wyświetlane byłyby tylko zmienione znaki. Bardzo to by przyspieszyło całą operację związaną z wyświetlaniem bo jeśli mamy znak który np pulsuje co sekundę to tylko ten znak jest negocjowany z LCD do wyświetlenia... Nie wiem czy to zrozumiale napisałem.

    OdpowiedzUsuń
    Odpowiedzi
    1. Tak ja bardzo dobrze rozumiem co masz na myśli pisząc o tym buforku ... ale że tak powiem, gdyby zmieniały się nawet tylko np 3 znaki co sekundę w różnych miejscach, to wyświetlenie każdego z nich na swojej pozycji zajęłoby już chyba dłużej niż wyświetlenie 2 całych kolejnych linii jednym ciągiem ....

      ale z drugiej strony (myślę tak na gorąco nad tym) ... to może być bardzo ciekawy pomysł ... bo rzeczywiście sprawdzanie czy coś się zmieniło pomiędzy tym buforkiem a ekranem (a taka operacja tylko na pamięci RAM procka może być bardzo szybka) może powodować że nie musi się co każde 20ms odświeżać ekran co przede wszystkim daje więcej czasu dla procesora - dla całej pętli głównej ;) ... to też dosyć spora korzyść może wyjść.

      jak będę miał czas z przyjemnością pobawię się tym twoim dodatkowym cennym zresztą pomysłem ;) a przy okazji fajnie słyszeć kolejny głos zrozumienia dla takich metod.

      Usuń
  13. Jedyną wadą chyba takiego buforka są te właśnie czasy odświeżania, bo od ilości zmian będzie się ten czas wahał. Ale wszystko można wymierzyć, ewentualnie dzieląc procedure wyświetlania/odświeżania na części w zależności od ilości zmian na LCD.

    Pomysł mi ten zaświtał bo z doświadczenia wiem że na każdym komputerze komunikacja poprzez porty zawsze najwięcej czasu zamowała. Dodam że te procedurki odpowiedzialne za te warstwy można się pokusić i napisać w asemblerze (jesj powód do nauczenia się języka maszynowego) to w ogóle by była żyleta program - mały szybki.

    Acha rozwiązanie z warstwami załatwia nam jeszcze sprawę standaryzacji komunikacji z LCD, bo niedawno kupiłem sobie taki wyświetlacz 4x20 i okazuje się że żeby coś wyświetlić w 3 lini trzeba kursor ustawić na współrzędną x=21 i y=0 (sterownik Radzia z 2007 roku tak robi). A teraz jak mam warstwy to tylko w jednym miejscu w programie wprowadzam wzmianki i program działa nadal na większym wyświetlaczu.

    OdpowiedzUsuń
  14. Antystatyczny11 lipca 2012 20:10

    Świetna metoda wyświetlania! Spróbuję jej użyć korzystając z Bascoma. Pozdrawiam :-)

    OdpowiedzUsuń
    Odpowiedzi
    1. Jak najbardziej, kiedyś programując w Bascomie także jej używałem (tej metody), wyniki są rewelacyjne. Potwierdzam.

      Usuń
  15. Witam serdecznie :)
    Panie Mirosławie, owszem zgodzę się Z Panem, iż opisana metoda jest dobra (na miarę potrzeb).
    Lecz co jeśli mamy w projekcie wyświetlacz TFT np. 320 * 240 pikseli ?

    A co gorsza operujemy na grafice ?

    Niestety w przypadku tylko 1/4 powierzchni takiego wyświetlacza, czyli 80 * 60 pikseli i do tego składowa koloru RGB (R + G + B) jest to wręcz nieodpowiednia metoda z powodu małej pamięci np. ATmegi644.
    W takim przypadku musimy sięgać do pamięci sterownika samego wyświetlacza (SSD 1963, lub innego, odpowiednie rejestry).
    Pana rada jest bardzo dobra dla osób, które zaczynają się uczyć, to fakt, lecz jeśli Ktoś chce chce stworzyć projekt z graficznym interfrejsem użytkownika (np. pasek zadań w Windows XP) napotka wielkie problemy ...

    Pozdrawiam serdecznie :)

    OdpowiedzUsuń
    Odpowiedzi
    1. hahaha ;) no no ;) pewnie TFT i warstwy na 8-bitowym procku to gorzej niż HARCORE ;) .....

      Tylko czy kolega nie zauważył, że temat warstw dotyczy HD44780 ??? ;) gdzie tu mowa w ogóle o graficznych wyświetlaczach a co dopiero kolorowych TFT ;)

      Owszem będzie to możliwe ale przy użyciu ARM i to taktowanego solidną częstotliwością. Wtedy warstwy jak najbardziej ;) .... hahaha no ale nie na 8-bitowcach

      tu kolega ma całkowitą rację

      Usuń
    2. Szanowny Panie Mirosławie (Autorze mądrych książek) :)
      W przypadku grafiki na zewnętrznym kontrolerze wyświetlacza mikroprocesor, którym sterujemy wyświetlacz (SSD...) nie męczy się aż tak bardzo by przymulić pracę np. ATmegi 664*(tylko grafika), ATmega jest obarczona jedynie wysyłaniem komend do SSD (po wcześniejszym zainicjowaniu rejestrów kontrolera SSD... ).
      Tak więc moim skromnym zdaniem nawet Bascom potrafi wiele, jeśli potrafimy programować mikro kontrolery.
      Owszem, istnieje możliwość, by bez prze taktowanie procesora np, ATmegi 644 do 32 MH by uzyskać 25 klatek na s, lecz czy jest to warte uwagi ?
      Tak robią jedynie adepci MC .

      Usuń
  16. A tam zaraz mądrych ;) ...

    Jednak nie zgodziłbym się do końca że używając nawet tak dobrego sterownika jak SSD1963 uda się nam zrobić efekty z warstwami tzn na tej zasadzie warstw jakie opisałem dla HD44780, żeby to nie "przymuliło" procesora :( Owszem sterownik SSD1963 będzie sobie potrafił super szybko przełączać obrazy .... ale już czas na załadowanie do sterownika bitmap RGB przez 8-bitowiec niestety ale będzie obarczone sporym czasem, więc nie da rady.

    tzn może dla jakichś małych okienek ;) pewnie by dało radę nawet nie próbowałem choć miałem ochotę - tylko czasu nie starczyło ...ale na końcu to i tak by wyszło tak jak kolega mówi - po prostu sztuka dla sztuki przy 8-bitowcu. Więc trudno się z tym nie zgodzić.

    OdpowiedzUsuń
  17. Tak więc Panie Mirosławie doszliśmy do ostatecznego wniosku, iż procesory typu ATmega644
    nie dadzą sobie rady z tak wielkim wyzwaniem, mimo przetaktowania.

    Mam nurtujące mnie pytanie, mianowicie, czy istnieje możliwość sprzedaży przez firmę Atnel samego sterownika panelu dotykowego (kompletna płytka) ? bez konwerterów
    dla FTT ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Tzn bez konwerterów dla TFT pewnie chodziło (literówka) ale ok .... hmmm czyli bez scalaków 74LVC245 ??? czy o to chodzi ???

      Usuń
  18. Panie Mirosławie, zdaję sobie sprawę z tego, iż to nie jest forum, lecz Pański blog.
    Przepraszam za zadane pytanie, miałem na myśli sam sterownik panela dotykowego na układzie STMPE811 (kompletna płytka, nie koniecznie z modułem zasilania 3.3 V)

    OdpowiedzUsuń
    Odpowiedzi
    1. Nie ma sprawy - z tym że pytanie tutaj, żaden problem. Ale hmmm czyli chodziłoby o samą płytkę do STMPE811 ??? .... no tak, tylko że ciężko tak zaryzykować produkcję nawet małoseryjną tego typu, jeśli nie wiadomo jaki byłby odzew z rynku. W przypadku tej, która jest - to czułem że będzie zainteresowanie i jest. Nie duże bo nie duże ale akurat na małą serię.

      Czy może koledze chodzi np o tą samą wersję PCB ale np z obsadzonym tylko STMPE811 i jego aplikacją ? .... można byłoby to wtedy użyć pewnie do różnych paneli dotykowych, problem tylko pewnie by się pojawił czasem z gabarytami PCB .... no ale to właśnie takie wybory ... które determinują czy coś zacząć robić czy nie.

      Usuń
  19. Mam na myśli jedynie układ STMPE811 na płytce (wraz z opornikami, kond. łączami lutowniczymi itp), który jest odpowiedzialny za komunikacje MC z panelem za pomocą I2C, moim zdaniem szybkość tej komunikacji wystarcza do własnych zastowań :)

    OdpowiedzUsuń
  20. A może odpowiem w ten sposób ... mianowicie nie znam się na elektronice "na płycie", jedynie jak jak i co podłączyć i jakie zasilanie z gotowych modułów :(

    OdpowiedzUsuń
    Odpowiedzi
    1. Tzn nie za bardzo może rozumiem celu ? :(

      Generalnie przecież na naszej stronie:

      http://atnel.pl/atb-glcd-tft-konwerter.html

      podałem cały dokładny schemat - więc gdyby ktoś chciał we własnym zakresie sobie to zrobić to proszę bardzo - ja tam nie ukrywam schematów jak inne firmy ;)

      Natomiast produkcji samej płytki PCB tylko z układem STMPE811 raczej na razie się nie podejmę bo przygotowanie małoseryjnej produkcji to spore koszty a nie wiem jak wyglądałoby zapotrzebowanie. :( Nie chciałbym wydać sporo pieniążków i żeby leżały potem na magazynie i się kurzyły. Tak kiedyś miałem z zestawami ATB w wersji do samodzielnego montażu DIY.

      Usuń
  21. O kurde, jak mi przykro :(, a może poleciłby Pan mądrego elektronika, który wykonana takiego CUSIA
    ?

    OdpowiedzUsuń
  22. No pewnie każdy coś takiego wykona, to nie jest takie skomplikowane może poza samym lutowaniem tego układu STMPE811 ponieważ on jest jak widać na zdjęciach w obudowie QFN16 a to już zwykłą lutownicą nie da rady polutować ... Amatorsko też można mieć problemy z wykonaniem PCB ponieważ układ wymaga czegoś takiego jak "Thermal Pad" pod spodem obudowy więc bez metalizacji otworów może być ciężko. Dlatego mnie ciężko kogoś tak konkretnie polecić.

    OdpowiedzUsuń
  23. Na samym naglowku jest taki napis "LCD HD44870 - WARSTWY - EFEKTY" chyba powinno byc "HD44780" czy sie myle ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Hahaha - eeeh te moje "ukochane" literówki :) - oczewiście masz rację ;) nie mylisz się - już poprawiam i dzięki za zwrócenie uwagi.

      Usuń
  24. Witam ponownie Panie Mirosławie :)
    Mam pytanie dotyczące SSD1963 .
    Czy rozgryzł już Pan wszystkie rejestry tego kontrolera ?
    Nurtuje mnie pytanie, czy rejestr 2e (Read_memory_start) zwróci składową koloru pixela w RGB ?

    OdpowiedzUsuń
    Odpowiedzi
    1. Nie miałem niestety jeszcze czasu pobawić się odczytem, ale i tak będę musiał się wcześniej czy później za to zabrać.

      Jednak od razu mogę odpowiedzieć na to pytanie - odczyt pixela na pewno będzie domyślnie RGB ;) ale można sobie przecież w konfiguracji w razie czego przestawić na BGR ;)

      aha rozkaz 0x2e służy tylko do poinformowania sterownika SSD, że po nim będziemy odczytywać pixele od wskazanego adresu. Uprzednio wskazując ten adres komendami 0x2a i 0x2b. Przy odczycie działamy IDENTYCZMNIE jak przy zapisie ;) tylko że zamiast zapisywać DO to odczytujemy Z .... zmieniając stan linii RW

      Usuń
  25. Dobry jest ten sterownik :)
    Np. rejestr Set Post Proc (0xbc) odpowiedzialny jest za informacje dla sterownika, iż będą prowadzone zapisy dotyczące :
    1) jasności podświetlenia wyświetlacza (PWM)
    3) kontrastu
    4) nasycenia .

    A rejestr Get Post Proc (BD) zwróci nam wartości tych ustawień .

    OdpowiedzUsuń
    Odpowiedzi
    1. No nie pozostaje mi nic innego jak tylko potwierdzić, że SSD1963 to super przemyślana konstrukcja i dzięki temu także 8-bitowce mogą z powodzeniem korzystać z takich jakby nie patrzeć ogromnych wyświetlaczy ;) ... Pisząc ogromnych mam na myśli jednak spore ilości danych do przesyłania ;)

      Usuń
  26. Sterownik ten charakteryzuje się następującymi,
    wybranymi parametrami funkcjonalnymi:
    – wbudowana pamięć o pojemności
    1215 kB umożliwiająca obsługę wyświetlaczy
    TFT o maksymalnej rozdzielczości
    864×480 pikseli i 24-bitowej głębi
    kolorów,
    – obsługa 18- i 24-bitowej magistrali TFT,
    – obsługa 8-bitowego interfejsu danych
    RGB,
    – sprzętowa funkcja obrotu obrazu o 0, 90,
    180 i 270 stopni,
    – sprzętowa funkcja lustrzanego odbicia
    obrazu w poziomie i w pionie (mirroring),
    – sprzętowa funkcja definicji aktywnego
    obszaru pamięci ekranu z automatyczną
    inkrementacją (windowing),
    – sprzętowa funkcja regulacji parametrów
    jasności, kontrastu i saturacji ekranu,
    – sprzętowa funkcja automatycznej kontroli
    podświetlenia ekranu TFT (modulacja
    PWM),
    – obsługa interfejsów danych o szerokości
    8, 9, 16, 18, i 24-bitów,
    – wbudowany oscylator PLL.

    Informacje zaczerpnięte z "Obsługa wyświetlacza TFT .." autorstwa Pana Roberta Wołgajewa :)

    OdpowiedzUsuń
  27. "danych do przesyłania" w trybie SPI ?

    OdpowiedzUsuń
    Odpowiedzi
    1. A o jaki sterownik pytasz pan ? ;)

      Bo ani HD44780 ani SSD1963 nie obsługuje się w trybie SPI ;) ... tzn SSD1963 to chyba nawet dałoby radę ale to jest bez sensu.


      Tryb równoległy generalnie

      Usuń
  28. Witam serdecznie. Prawdę mówiąc rozumiem temat "warstwy", natomiast nie wiem jak to można zrealizować programowo - przepraszam ja na bascomie chowany..., nie mówiąc już o tym, że w sprawach programowania jestem cienki jak tyłek węża. Czy odświeżanie ekranu realizujemy cyklicznie np. w przerwaniu timera (coś jak w Pana książce - karuzelowy sposób szeregowania)i każdą linię z osobna, czy wszystkie naraz? Nie ukrywam, że mózg mi się gotuje gdy czytam kody dołączone do książki, ale o dziwo potrafię je uruchomić, niemniej jednak trudno mi to" warstwowe wyświetlanie" wyobrazić.

    OdpowiedzUsuń
    Odpowiedzi
    1. "Czy odświeżanie ekranu realizujemy cyklicznie" - tak - oczywiście że cyklicznie - więc już kolega sporo zaskoczył .... nie ma co się przejmować, ale doprecyzuję dalej

      .... cyklicznie ale broń boże w żadnym przerwaniu timera ponieważ operacje wykonywane na LCD są zbyt wolne aby je umieszczać w przerwaniu. Na szczęście nie jest to trudne - po prostu w przerwaniu ustawiamy sobie jakąś flagę np co 20ms - a w pętli głównej, która przecież kręci się tysiące razy szybciej sprawdzamy czy ta flaga jest ustawiona. Jeśli tak? no to wtedy wyświetlamy całe te linie bufora na LCD - dokładnie co 20ms, kasując jednocześnie flagę aby ten IF mógł być wykonany za kolejne 20ms

      więc jak widać - cykliczność wcale nie musi oznaczać wykonywania czegoś w przerwaniu

      to tak w dużym uproszczeniu

      a jeśli chodzi o szczegóły to opisuję to w swojej drugiej książce:

      http://atnel.pl/jezyk-c-pasja-programowania.html

      tylko hmmm jeśli kolega ją posiada - a dopiero początkuje w C .... to zdecydowanie lepiej byłoby zacząć przygodę z językiem C od mojej pierwszej książki czyli tej:

      http://atnel.pl/mikrokontrolery-avr-jezyk-c.html

      gorąco polecam

      Usuń
    2. No właśnie posiadam pierwszą Pańską książkę i dzięki niej wiele się dowiedziałem o procesorach i programowaniu. "C" unikałem jak ognia ale przedstawione przez Pana rozwiązania "przelewałem" do bascoma. Muszę się jednak przestawić i zmienić język programowania.

      A teraz konkretnie - tak właśnie sobie "wydumałem", że tak to powinno działać - timer i flagi, ale nie miałem pewności a głupio się ośmieszyć :(. Czyli "odświeżanie" wyświetlacza jest wykonywane TYLKO w pętli głównej po ustawieniu flagi w timerze (np co 20ms) bez względu na to, czy zmieniły się dane do wyświetlenia? Dobrze rozumiem?

      Usuń
    3. O widzisz Pan ;) ja .... gdy jeszcze programowałem kiedyś w Bascomie, robiłem dokładnie tak samo z tym buforowaniem i ala warstwami wyświetlacza. Więc jak się pan zaprzesz to i to zrobisz w Bascomie.

      Tylko proszę pomyśleć - że robiąc to w Bascomie to jest tak jakbyś się pan w srogą i mroźną zimę wybrał w zaciasnym kubraczku na antarktydę, podczas gdy robiąc to w C - będzie tak jakbyś założył pan wygodne i ciepłe futro ;) Wiem co mówię, bo ja Bascoma też zdążyłem poznać jak własną kieszeń i absolutnie nie mam klapek na oczach żeby mówić, że jest zły itp ... Do wielu rzeczy wystarczy, a szczególnie jak się go zacznie szprycować wstawkami asemblerowymi (co też kiedyś robiłem ochoczo) ...

      Ale szybko przyjdzie czas że okaże się że jest za ciasny do wielu projektów .... I tym bardziej będzie bolało i człowiek sobie będzie pluł w brodę później .... kurczę dlaczego wcześniej się nie zacząłem uczyć tego C ... proszę mi uwierzyć ja też tak miałem ;)

      -------------

      A teraz konkretna odpowiedź - oczywiście że tak jak myśli kolega ;) bez względu na to czy zmieniły się dane do wyświetlenia - to co 20ms wyrzucam ale UWAGA! całe dwie linie (jeśli wyświetlacz 2 wierszowy) albo całe 4 linie

      to oznacza, że po drodze w międzyczasie NIC A NIC nie wyświetlam na LCD !!! tylko wszystko że tak powiem "wyświetlam" ładuję do tego buforka ekranu ... - o to chodzi ;)

      i nie ma co być głupio - jak coś to pytać śmiało ;)

      ale w związku z takimi pytaniami to bardziej zapraszam na nasze forum

      www.forum.atnel.pl

      proszę zobaczyć jak tam jest przyjaźnie pytać się o takie rzeczy. Zapewniam, że nikt nie wyśmieje a każdy życzliwie pomoże

      Usuń
  29. "Bo ani HD44780 ani SSD1963 nie obsługuje się w trybie SPI ;) ... tzn SSD1963 to chyba nawet dałoby radę ale to jest bez sensu. "
    Istnieje wyświetlacz zgodny z HD44780 mający opcjonalnie interfejs SPI to DOGM 163 o organizacji 3 linie po 16 znaków.
    wybór interfejsu następuje sprzętowo przez podanie napięcia, lub ściągnięcie do masy odpowiedniej końcówki wyświetlacza.
    Natomiast bardzo ciekawa alternatywa dla standardowych wyświetlaczy z HD44780, są wyświetlacze OLED, na zmodyfikowanej wersji HD44780, które dodatkowo udostępniają tryb graficzny, dla 2x16 znaków jest to 16x100 pixeli.

    OdpowiedzUsuń
  30. Ja mam takie pytanie:
    jak to się dokładnie odbywa?
    Dajesz bufor na określoną ilość znaków i je wyświetlasz od danej pozycji?
    Nie bardzo to rozumiem bo jak wiesz Mirku działam w bascomie.
    matrix z forum atnel.pl

    OdpowiedzUsuń
    Odpowiedzi
    1. Wiesz jak kiedyś programowałem w Bascomie to DOKŁADNIE ten sam mechanizm wykorzystywałem tyle że mniej warstw się wyrabiało w jednym czasie. Ale generalnie działało też ładnie.

      Daję bufor w RAM na cały ekran LCD czyli dla 2x16 to będą 32 bajty tylko

      ten bufor wyświetlam na LCD co np 40ms

      a wszystkie operacje w programie nie wykonuję na LCD tylko na tym buforze. Więc łatwo sobie wyobrazić że takich buforów można mieć kilka (warstwy) i łączyć je razem do jednego głównego, który jest wywalany na LCD co 40ms

      Usuń
    2. Czyli jednym słowem cały ekran z danymi zastępuje cały ekran z danymi, tak?
      Niejako warstwa podmienia warstwę ?
      matrix

      Usuń
    3. No dokładnie - tyle że warstwa nie musi podmieniać warstwy zawsze - można je mieszać w dół ;) ... albo co ciekawe wyświetlać mniejsze warstwy i na różnych współrzędnych. dzięki temu scrolowanie w dowolny sposób to poezja

      Usuń
    4. Mieszać w dół? Czyli jak?

      Co do mniejszych i większych warstw to i tak trzeba zamieniać jedną na drugą bo będą zostawać niepotrzebne znaki.
      Oczywiście nie piszę o ciągłym wyświetlaniu. Bo w przypadku ciągłego wyświetlania większej warstwy pozostałe znaki podczas wyświetlenia warstwy mniejszej nie będą miały znaczenia.
      matrix

      Usuń
    5. No widzisz - jak to robić to już tak ciężko wyjaśnić w kilku zdaniach - generalnie - żadne znaki niepotrzebne nie będą przeszkadzać bo te co są poza obszarem aktywnym podczas mieszania warstw i tak się ignoruje. Mieszanie ? ... no spójrz na ten rysunek na początku artykułu - jeśli spojrzeć tak na te warstwy to po prostu w dół się je umieszcza w głównym buforze i zawsze ta co jest na wierzchu ew zakrywa tą pod spodem chyba że jest przesunięta - albo ma obszary przeźroczystości jeśli takie wprowadzimy ;) ... można np ustalić że spacje są przeźroczyste w dół i wtedy jeśli na warstwie na górze będą gdzieś spacje to zobaczymy w tym miejscu to co jest na warstwie niższej

      to wszystko się już realizuje za pomocą kilku funkcji obsługujących taki mechanizm. A że wszystkie funkcje działają tylko na pamięci RAM mikroklocka to są to operacje tak szybkie że można ;) mieszać ile się chce na nich ;)

      Usuń
  31. Tak się zacząłem zastanawiać co by tu uprościć ;) i do głowy przychodzi mi nieco inne rozwiązanie. W zasadzie to też jest oparte na buforze tyle, że na jednym. Gdyby tak przerobić po prostu funkcję wyświetlania na lcd i nazwać ją np. "LCD_BUF x,y,zmienna" czyli podobnie jak standardowe wyświetlanie ma ekranie lcd tyle, że zmienne leciały by do bufora pamięci już w miejsce wyliczone przez pozycję x i y, a samo przepisanie czyli odświeżenie co jakiś czas (20-40ms) właściwego lcd realizowane gdzieś pod koniec pętli, np. przez polecenie LCD_REWRITE z tym, że co ważne(!) po każdym odświeżeniu ekranu bufor byłby z urzędu czyszczony spacjami. Dzięki temu jeśli chcielibyśmy coś wyświetlić to po prostu wpisujemy coś cały czas do bufora pierwszym poleceniem, a w momencie gdy napis już nie potrzebny to po prostu nic nie wpisujemy (nie musimy wstawiać spacji !). Pozostaje tylko kwestia priorytetów - "na wierzchu" w danej pozycji pozostaje zawsze znak, który był wpisany jako ostatni, ale nie widzę żeby to był jakiś znaczący problem. Na pewno zaletą takiego rozwiązania jest mniejsze użycie pamięci, a i odpada sklejanie kilku buforów :)
    Nie wiem czy zrozumiale opisałem ideę, ale myślę, że da się zrozumieć o co mi chodzi ;)
    Pozdrawiam. SylwekK

    OdpowiedzUsuń
    Odpowiedzi
    1. Dokładnie o to mniej więcej chodzi w tej koncepcji ;)

      Usuń
  32. Witam
    Ja mam takie pytanie czy jest gdzieś dostępny cały kod dotyczący warstw przedstawiony na filmie?
    Pozdrawiam
    Heniu

    OdpowiedzUsuń
    Odpowiedzi
    1. proszę bardzo - tutaj:

      http://atnel.pl/jezyk-c-pasja-programowania.html

      i to nie tylko sam kod ale i mnóstwo ważnych opisów jak do tego podchodzić

      Usuń
  33. Dzięki, to się domyślałem i na pewno kupię.
    Poznaję LCD i posiadam pierwszą książkę, zabieram się za napisanie menu do obsługi zegara na PCF 8583 i zaciekawił mnie temat warstw dlatego pytałem o kod.
    Dziękuję za pomoc.

    OdpowiedzUsuń
  34. Witam mirQ
    Świetny artykuł niemniej jednak jeszcze nie przeszedłem na C i zastanawiam sie nad kupnem Twojej/innej książki. BTW wracając do tematu
    Próbuję dokładnie to samo zrobić w bascomie i wychodzi na to że uproszczone programowanie nie zawsze pozwala na to samo osiągniemy w C.
    Jest problem taki gdy przesuwam daną wartość/znak/cokolwiek na wyświetlaczu alfanumerycznym to na segmencie z którego go przesuwam wciąż on zostaje widoczny.Efekt jest taki że dana wartość się przesunęła i odlicza dalej ale zostawia jakby wartość wyświetloną w poprzedniej ramce 1 znaku wyświetlacza.
    Dopiero jak użyje CLS to problem znika za to wyświetlacz zaczyna mrugać w rytm odświeżania informacji wklepywanych do wyświetlacza (niech to będzie np 10hz)
    to widać i to ewidentnie
    Czy da się jakoś wyeliminować problem ?

    Pozdrawiam i z góry dziękuje
    Tomek - DJMANIAK@vp.pl

    OdpowiedzUsuń
    Odpowiedzi
    1. Szkoda że z youtuba usunęli mi stary film który zrobiłem jeszcze z czasów gdy sam pisałem w Bascomie. A wiesz co na nim było ? - DOKŁADNIE TO SAMO ;) czyli efekty na LCD dzięki zastosowaniu warstw ;) ... a więc odpowiedź na twoje pytanie - czy da się tak zrobić w Bascomie brzmi TAK ...

      jednak odpowiedź na szczegółowe pytanie jak wyeliminować jakiś tam problem który masz ??? hmmm kompletnie nie umiem tu czegoś podpowiedzieć bo jak ? w oderwaniu od kodu ? ... ale nawet gdybyś miał pokazać kod - to i tak bym nie miał możliwości go przeanalizować z dwóch powodów:

      1. brak czasu
      2. totalnie już zapomniałem bascoma i musiałbym się go specjalnie do tego celu chyba od nowa uczyć co spowodowałoby jeszcze większą stratę czasu

      ale myślę, że skoro wiesz że da radę i że to jest uniwersalny mechanizm - to poradzisz sobie

      natomiast dwie wskazówki dla ciebie

      ZAPOMNIJ W OGÓLE NA ZAWSZE o poleceniu CLS do LCD nie ważne czy piszesz w Bascomie czy w C ... rozumiesz ? zapomnij ;)

      Używaj LCD z podłączonym pinem RW do procka a nie do GND ! czyli sprawdzaj flagę zajętości bo przydaje się spora szybkość na LCD dla tych operacji

      tyle

      Usuń
    2. Wiem, wiem już i tak dzięki Twoim artykułom przeszedłem z wait na timery czy nawet proste funkcje w stylu zmienna = zmienna +1 if zmienna >255 than LCD " bla bla "
      Wait po prostu niszczy wszystko nomen omen ten probelm jest znacznie bardziej toporny.
      Pozdrawiam

      Usuń
    3. Sam pytając czujesz że to będzie tak troszkę ... sam wiesz

      Usuń