[086] Arduino hackuje panele urządzeń - cz. 2
Struktura klawiaturki hackowanego w poprzednim artykule monitora jest bardziej chytra, niż się wydaje. Tak naprawdę mamy tutaj dwie linie, a nie sześć i dzielniki rezystorowe, tak dobrane, że zwieranie tych linii wymusza pojawienie się trzech przedziałów napięć. Więc wystarczyłoby nam się podłączyć dwoma liniami, ale analogowymi. Podawalibyśmy konkretne napięcie i układ interpretowałby to jak wciśnięcie konkretnego przycisku. To jest sposób, ale nieco skomplikowany. Należałoby scałkować przebiegi PWM i dobrać szybkość zmian tak, by nie były rejestrowane napięcia przejściowe. Zrezygnowałem z tego pomysłu na rzecz sześciu linii sterowanych transoptorami.
Tak sobie to będzie wyglądać. Po złożeniu całości po kolei podrzucałem napięcie na kolejne transoptory. W dwóch przypadkach… nic się nie działo. Myślałem, że mam uszkodzone elementy, więc podmieniłem wyprowadzenia i – nadal nic z tego. Po pomiarach okazało się, że napięcie spada do 100 mV, a klawiatura reaguje dopiero, gdy spadnie do 50 mV. Wymieniłem więc rezystory sterujące diodami w transoptorach z kilooma na 220 omów, dające zdecydowanie duży prąd 15 miliamperów, ale dopuszczalny dla wszystkich ogniw układu. Napięcie spadło do 60 mV, interfejs już działał, ale nie zawsze. Sytuacja wydawała się beznadziejna. Wysypałem wszystkie transoptory z szufladki i rozpocząłem testy. W końcu znalazły się takie, które dają mniej niż 40 mV, a to wystarczyło do pewnej pracy klawiatury. Uff, można będzie działać dalej. A co w przypadku, gdyby się nie udało? Cóż, najprościej byłoby użyć przekaźników. Są takie, które zadowolą się prądem 30 mA, co – może nieco niezgodnie ze sztuką, ale praktyka pokazuje, że tak można – dałoby się sterować prost z pinów Arduino, które dopuszczają 40 mA, acz z adnotacją, iż nie zaleca się. Na szczęście nie muszę tego sprawdzać, ale jeśli ktoś byłby do tego zmuszony, jest rzeczą bardzo ważną, by nie zapomniał o diodach gaszących przepięcia na każdej z cewek przekaźnika.
A najlepiej po prostu nie dziadować, tylko użyć ULN-a. Innym rozwiązaniem byłoby nawiązanie bardziej inteligentnej współpracy z układem – za pomocą wspomnianej generacji napięć i do tego kiedyś przejdę. W końcu należałoby użyć tak zwanych transoptorów MOS, które cechują się wyjątkowo niskim oporem podczas włączenia. Aczkolwiek i to trzeba byłoby sprawdzić, gdyż gwarancji – przy tak nieoczywistych próbach nawiązania współpracy z systemem zastanym – nie ma. Skoro jednak wszystko działa, trzeba będzie przejść do kolejnego etapu.
Etap III - co by tu poprawić?
Czas zastanowić się nad tym, co w ogóle chcemy usprawnić. Zacznijmy od tego, że mamy dostęp do wszystkiego, co znajdziemy w menu, nawet jeśli będzie to głęboko ukryte.
Zatem ten etap zaczyna się wędrówką po wszelkich zakamarkach i spisaniem rzeczy użytecznych. Na pewno zechcemy regulować kontrast, jasność i nasycenie, być może też ostrość. Może także balans kolorów. Ale to nie jest artykuł o monitorku, a o sposobach, więc ograniczę się do kilku przykładowych opcji, a resztę już każdy będzie sobie mógł zorganizować w oparciu o własne urządzenie i potrzeby. Spróbujemy użyć dóbr dostępnych na płytce edukacyjnej TME, która daje mi dostęp do pięcioprzyciskowej klawiaturki i potencjometru.
To oczywiście bardzo skromne zasoby, ale dla ukazania metod – wystarczy. W oparciu o nie stworzę sobie możliwość szybkiej regulacji: jasności, kontrastu i nasycenia, wybór poziomu odszumiania obrazu i regulację tak zwanego blendingu. Pięć przycisków posłuży mi do wyboru opcji, a potencjometrem będę wybierał ich wartość.
Etap IV - ustalenie maksymalnej szybkości pracy interfejsu
Każdy „ludzki” interfejs musi być zabezpieczony przed niepewnością zachowań ludzkiej ręki i związanych z nią manipulatorów. Innymi słowy, trzeba zabezpieczyć układ przed drganiami styków i niekontrolowanym powielaniem się wciśnięć. Pisałem o tym kiedyś w artykule poświęconym obsłudze przycisków. Takie zabezpieczenia wprowadzają zawsze pewien minimalny czas, wymagany do zarejestrowania wciśnięcia i drugi, który określa przerwy między wciśnięciem przycisków, by te były rejestrowane jako dwa oddzielne. Czeka nas więc ustalenie tych czasów na drodze empirycznej. Jak się do tego zabrać? Napiszemy prosty szkic, który będzie testował wciskanie jednego z bocznych przycisków.
const byte wPrawo = 9; // Numer pinu, do którego podłączony jest przycisk "w prawo".
void setup() {
pinMode(wPrawo, OUTPUT); // Zadeklaruj porty przycisków jako wyjścia.
}
void loop() {
digitalWrite(wPrawo, HIGH); // Wciśnij przycisk.
delay(100); // Dobierz najkrótszy czas, przy którym za każdym razem wartość się zwiększa.
digitalWrite(wPrawo, LOW); // Puść przycisk.
delay(1000); // Zaczekaj.
}
Na razie wejdźmy w menu monitora klasycznie – klawiaturką urządzenia, odnajdźmy zmianę kontrastu i zjedźmy nim do minimum, czyli do zera. W szkicu zadeklarujemy jeden na razie przycisk wPrawo, czyli zwiększający wyświetlaną wartość. W nieskończonej pętli będziemy go wirtualnie wciskać na pewien czas, po czym puścimy go i zaczekamy sekundę. Na początek proponuję wstawić wartość 100 ms i kolejno zmniejszać ją dwukrotnie, dopóki na wyświetlaczu przestaną się rejestrować zmiany. U mnie nastąpiło to przy wartości 1 ms. Zwiększamy tę wartość odrobinę. Przy 2 ms wszystko już się rejestrowało poprawnie. Należy wówczas zwiększyć czas o kolejną milisekundę – tak dla bezpieczeństwa i od tej chwili 3 ms to będzie nasz minimalny czas wciśnięcia klawiatury. Czas na sprawdzenie z jaką maksymalną szybkością możemy wciskać przyciski raz po raz, by rejestrowały się jako niezależne wciskania.
const byte wPrawo = 9; // Numer pinu, do którego podłączony jest przycisk "w prawo".
const byte wLewo = 10; // Numer pinu, do którego podłączony jest przycisk "w lewo".
void setup() {
pinMode(wPrawo, OUTPUT); // Zadeklaruj porty przycisków jako wyjścia.
pinMode(wLewo, OUTPUT);
}
void loop() {
for (byte x = 0; x < 101; x++) { // Powtarzaj 101 razy.
digitalWrite(wPrawo, HIGH); // Wciśnij przycisk.
delay(3); // Dobierz najkrótszy czas, przy którym za każdym razem wartość się zwiększa.
digitalWrite(wPrawo, LOW); // Puść przycisk.
delay(100); // Dobierz najkrótszy czas, przy którym system interpretuje osobne wciśnięcia.
}
for (byte x = 0; x < 101; x++) { // Powtarzaj 101 razy.
digitalWrite(wLewo, HIGH); // Wciśnij przycisk.
delay(3); // Dobierz najkrótszy czas, przy którym za każdym razem wartość się zwiększa.
digitalWrite(wLewo, LOW); // Puść przycisk.
delay(100); // Dobierz najkrótszy czas, przy którym system interpretuje osobne wciśnięcia.
}
}
W tym celu stworzymy sobie dwie pętle, a każda będzie się wykonywała 101 razy – bo tyle wartości mogą przyjmować zmienne kontrastu, jasności czy nasycenia. W pierwszej co ustalony czas będziemy wciskać prawy przycisk, w drugiej – lewy. Zacznijmy od opóźnienia równego 100 ms. Wartość ładnie się zwiększa od zera do stu, by następnie zmniejszać się, ale nie do zera, bo kilka wciśnięć po drodze gdzieś przepadło. Czyżby 100 ms było wartością zbyt krótką? Niedobrze. Spróbujmy ją zwiększyć. Przy 500 ms zaprzestałem doświadczenia. Okazuje się, że wciśnięcia klawisza wLewo raz na jakiś czas są ignorowane niezależnie od czasu wciskania i staranności, z jaką się to zrobi, także z lokalnej klawiaturki. Witaj jakości niemarkowych urządzeń! Co to oznacza? Czy cały pomysł jest do bani i nie ma sensu?
Etap V - wybór idei sterowania
Takie „połykanie impulsów” eliminuje nam użycie potencjometru. Położenie gałki potencjometru bowiem jednoznacznie określa stan zmiennej, a jeśli niektóre impulsy będą przepadać bez wieści, szybko nam się rozkalibruje to, co ustawimy gałką względem tego, co zinterpretuje monitorek. Ale nie wszystko stracone. Zamiast potencjometru użyjemy enkodera. Enkoder udaje potencjometr z tą różnicą, że jest gałką obracającą się bez końca, a więc jej położenie nic nie mówi o wartości aktualnej. Obracanie w prawo zwiększa stan zastany, w lewo – zmniejsza. Dla naszych celów tyle wystarczy, a zapomniane od czasu do czasu impulsy będą niezauważalne w praktyce. Innymi słowy – enkoder sprawę rozwiąże pomyślnie. A jak – o tym napiszę w kolejnym artykule.