Powrót

Kurs Arduino – Biblioteki dla MAX7219

Kontynuujemy przygodę w matrycowymi wyświetlaczami LED ze sterownikiem MAX7219.

W poprzednim odcinku (UR020a) wykorzystywaliśmy biblioteki stworzone przez Marco Colli (MajicDesigns) z Australii, dostępne w Githubie (https://github.com/MajicDesigns). Oprócz MD_MAX72xx,  biblioteki do obsługi sprzętu, używaliśmy też biblioteki MD_Parola, która jest swego rodzaju rozszerzeniem, nakładką i pozwala w prosty sposób uzyskać najróżniejsze efekty tekstowe. Zmodyfikowane przykładowe szkice z tej biblioteki dostępne są jako szkice 2003.ino oraz 2004.ino.

Fotografia 8

Jak już wiemy, szkice pracowały w modelu pokazanym na fotografii 8, ale podczas pracy szkicu 2004.ino działanie klawiszy zmieniających ustawienia było dziwne, niepewne. Teoretycznie bez powodu program sygnalizował na ekranie komputera nieustanne zmiany napięcia na nóżce A5. Tani chiński potencjometr aż tak dużych szumów na pewno nie miał. Podejrzenie padło na obwody zasilania.

W większości dotychczasowych eksperymentów z Arduino Uno zasilamy system przez kabel z gniazda USB komputera. Wyświetlacz przy pełnej jasności pobiera duży prąd, co może powodować fluktuacje zasilania. Wprawdzie napięcie zasilające potencjometr jest jednocześnie napięciem odniesienia dla przetwornika ADC, więc teoretycznie fluktuacje napięcia zasilającego nie powinny zmieniać pomiaru napięcia z dzielnika – potencjometru. Owszem, ale tylko przy powolnych zmianach napięcia zasilania. Natomiast szybkie zmiany – skoki napięcia zasilania mogą wpływać na wyniki pomiaru napięcia. Nie mierzyłem, ile prądu pobiera wyświetlacz, natomiast zbadałem oscyloskopem zmiany napięcia zasilania.

Rysunek 9 pokazuje, jakie były te zmiany w moim układzie po naciśnięciu S6 (negatyw). Jak widać, napięcie zasilające zmieniało się skokowo od około 4,1V do około 4,9V, czyli skoki miały amplitudę międzyszczytową około 0,8V, co stanowi aż 16% napięcia zasilania. Nic dziwnego, że powodowało to problemy.

Rysunek 9

Skutecznym rozwiązaniem zapewne byłoby zastosowanie zewnętrznego „sztywnego” zasilacza stabilizowanego. Ja jednak go nie zastosowałem. Problem nie tyle rozwiązałem, co ominąłem programowo: autor szkicu przytomnie wprowadził procedurę, która za zmianę stanu suwaka uznaje znaczącą zmianę napięcia.

Szkic 8

(…)
const uint16_t PAUSE_TIME = 1000; // in milliseconds
// const uint8_t SPEED_DEADBAND = 5; // in analog units
const uint8_t SPEED_DEADBAND = 10; //były problemy podczas pracy
(…)
void doUI(void) { // najpierw zbadaj napięcie z potencjometru
  // set the speed if it has changed – Analog read
  { // uzyskujemy wynik pomiaru z zakresu 0…100:
   int16_t speed = map(analogRead(SPEED_IN), 0, 1023, 0, 100);
   if ((speed >= ((int16_t)P.getSpeed() + SPEED_DEADBAND)) ||
       (speed <= ((int16_t)P.getSpeed() – SPEED_DEADBAND)))
    {       P.setSpeed(speed);
      DEBUG(„\nChanged speed to „, P.getSpeed()); }  }    (…)

Jak pokazuje szkic 8, wartość z przetwornika ADC z zakresu 0…1024 zostaje zmapowana na wartości z zakresu 0…100. W zmiennej speed (prędkość) mamy więc wartość z wcześniejszego pomiaru z zakresu 0…100. Następnie sprawdzane jest, czy świeżo odczytana prędkość jest większa lub mniejsza od wcześniejszej o więcej niż wynosi zadeklarowana wcześniej wartość SPEED_DEADBAND, która określa zakres nieczułości. W oryginale wartość SPEED_DEADBAND wynosiła 5,co daje ±5% pełnego zakresu. Zmieniłem ją na 10 i problem stał się nieznaczący (ale nie został rozwiązany).

Nadal reakcja przycisków była nieco dziwna, ale to oddzielna kwestia. W każdym razie naciskając przyciski oraz obserwując konsolę monitora i zmiany na wyświetlaczu LED, można było przetestować 27 dostępnych efektów, zmienianych przez naciskanie S5. Jeżeli przeprowadzisz takie testy, zwróć uwagę na problem obcinania dłuższych napisów. Rozwiązaniem są efekty numer 3 i numer 4, gdzie wykorzystane jest przewijanie napisów.

Naciśnięcie S6 wyświetla obraz w negatywie, gdy prawie wszystkie diody świecą. Właśnie wtedy pobór prądu jest największy i prawdopodobieństwo zakłóceń też największe. Zanim naciśniesz S6, naciśnij i przytrzymaj S4, co zmieni jasność. Proponuję ustawić jasność = 1, jak widać na rysunku 10.

Rysunek 10

Przy jasności 0 wyświetlacz jest całkowicie wygaszony (shutdown).

Niezależnie od tego, czy zrealizujesz układ według rysunku 7, koniecznie obejrzyj następujące autorskie filmy:

https://www.youtube.com/watch?v=JgzVCSFaz3I

https://www.youtube.com/watch?v=u1iELyROjW8

https://www.youtube.com/watch?v=tfwAHx0MTxU

https://www.youtube.com/watch?v=DUwtmrIVH58.

Możliwości opisanych właśnie bibliotek i przykładowych programów są ogromne. Nic dziwnego, że w Internecie jest wiele przykładów wykorzystania biblioteki MD_Parola, zawsze współpracującej z MD_MAX72xx. Być może i Ty zechcesz wykorzystać dostępne w niej efekty.

Możliwości jest mnóstwo, jednak opisanie zasad działania, dostępnych funkcji, ich argumentów i innych szczegółów byłoby zbyt obszerne, jak na jeden odcinek kursu Arduino. Jeżeli chcesz użyć tych bibliotek, poszukaj przykładowych szkiców najbliższych Twoim potrzebom i zmodyfikuj je według potrzeb.

Znajdziesz różne, w tym bardzo oryginalne i  interesujące rozwiązania. Sam autor tych bibliotek na stronie: https://github.com/MajicDesigns/WordClock-MAX7219

prezentuje zegar pokazujący czas słowami (WordClock). Na jedną jedyną matrycę LED 8×8 naklejona jest maska z literami, które odpowiednio zaświecane informują o aktualnym czasie. Fotografia 11 pochodzi ze strony https://raw.githubusercontent.com/MajicDesigns/WordClock-MAX7219/master/media/photo/WordClock%20Prototype.JPG,

w skrócie https://bit.ly/357foBr.

Fotografia 11

Trzeba podkreślić, że omawiane biblioteki, jak na Arduino, są wyjątkowo dobrze udokumentowane. W samych bibliotekach i przykładach są obszerne komentarze i wyjaśnienia. Jeszcze szersze opisy dostępne są na blogu:

https://arduinoplusplus.wordpress.com/

Zapewne w związku z opisanymi ćwiczeniach zainstalujesz też wymaganą w niektórych przykładach kolejną bibliotekę MD_UISwitch. Warto wiedzieć, że może się ona okazać bardzo przydatna w licznych zastosowaniach. Oprócz pojedynczych przycisków dołączanych do pinów Arduino, biblioteka obsługuje klawiatury matrycowe, a także  można ją wykorzystać w przypadku przycisków nietypowo dołączonych, na przykład  za pośrednictwem kostki CMOS 4017 według rysunku 12.

Rysunek 12

Rysunek 13

Zawarty w tej bibliotece rysunek 13 pokazuje sprytny i oszczędny sposób dołączenia pięciu przycisków do jednego wejścia analogowego. Biblioteka zawiera też przykładowy szkic dotyczący kalibracji takiego zestawu. Takie rozwiązanie stosowane jest w dość popularnym szyldzie z wyświetlaczem LCD i pięcioma przyciskami (fotografia 14).

Fotografia 14

W Internecie można znaleźć podobne propozycje dołączenia znacznie większej liczby przycisków i klawiatur, ale wtedy rośnie ryzyko błędów interpretacji, wynikających z tolerancji rezystorów oraz zmian  termicznych i długoczasowych. Pięć przycisków według rysunku 13 plus indywidualna kalibracja zapewnią  niezawodność i brak błędów.

Obsługując przyciski, biblioteka pozwala rozróżniać naciśnięcie  zwykłe, długie oraz podwójne (dwuklik), a można też ustawić opcję auto repeat. W zasadzie badamy temat wyświetlaczy matrycowych i graficznych, ale jeśli mamy już zestawiony układ według rysunku 7 i fotografii 8, wykorzystajmy zawarte w niej bardzo pouczające przykłady. Możesz też odłączyć wyświetlacz według rysunku 15.

Rysunek 15

Ja w pliku MD_UISwitch_Multi_Digital.ino dokonałem zmian według szkicu 9 i zapisałem jako A2005.ino.

Szkic 9:

// define pin numbers for individual switches
// const uint8_t SW_PIN[] = { 4, 5, 6, 7 }; // tak było
   const uint8_t SW_PIN[] = {2, 3, 4, 5, 6, 7 }; //mamy 6 przycisków , dołączonych do pinów 2…7
(…)
switch (state)
{ /* tak było w oryginale –
case MD_UISwitch::KEY_NULL:      /*Serial.print(„KEY_NULL”);*/ /*  break;
case MD_UISwitch::KEY_UP:        Serial.print(„KEY_UP”);        break;
case MD_UISwitch::KEY_DOWN:      Serial.print(„KEY_DOWN”);      break;
case MD_UISwitch::KEY_PRESS:     Serial.print(„KEY_PRESS”);     break;
case MD_UISwitch::KEY_DPRESS:    Serial.print(„KEY_DPRESS”);    break;
case MD_UISwitch::KEY_LONGPRESS: Serial.print(„KEY_LONGPRESS”); break;
case MD_UISwitch::KEY_RPTPRESS:  Serial.print(„KEY_RPTPRESS”);  break;
default:                         Serial.print(„KEY_UNKNWN”);    break;   */
// zmieniamy opisy na polskie:
case MD_UISwitch::KEY_NULL:     /*Serial.print(„brak naciśnięcia”);*/ break; //możesz odkomentować
case MD_UISwitch::KEY_UP:        Serial.print(„zwolnienie”);        break;
case MD_UISwitch::KEY_DOWN:      Serial.print(„naciśnięcie”);      break;
case MD_UISwitch::KEY_PRESS:     Serial.print(„interpretacja = normalne naciśnięcie”);     break;
case MD_UISwitch::KEY_DPRESS:    Serial.print(„interpretacja = \”dwuklik\””);    break;
case MD_UISwitch::KEY_LONGPRESS: Serial.print(„interpretacja = długie naciśnięcie”); break;
case MD_UISwitch::KEY_RPTPRESS: Serial.print(„interpretacja = autopowtarzanie (auto repeat)”); break;
default:                      Serial.print(„błąd – nierozpoznane działanie”);    break; (…)

 

Po skompilowaniu i wgraniu szkicu trzeba otworzyć na komputerze Monitor portu szeregowego (Ctrl+Shift+M),  gdzie pojawią się komunikaty dotyczące naciskania klawiszy (B0…B5). Działanie może wydać się dziwne z uwagi na ustawienia w bibliotece, a konkretnie w pliku …/src/MD_UISwitch.h, gdzie domyślne czasy są ustawione wbrew zaleceniom podanym w opisie tejże biblioteki, co może być mylące.

Otóż w tej bibliotece przewidziano skuteczną, ale dość skomplikowaną procedurę likwidowania skutków drgań styków (debounce), a dodatkowo  określono także czasy:

KEY_PRESS_TIME,

KEY_DPRESS_TIME,

KEY_LONGPRESS_TIME,

KEY_REPEAT_TIME,

które decydują o interpretacji naciśnięcia klawisza czy klawiszy.

Rozszyfrowanie znaczenia tych czasów na podstawie oryginalnego opisu ze strony https://bit.ly/3330lXF nie jest łatwe. Wątpliwości możesz wyjaśnić sam podczas testów. W bibliotece klasie przewidziane są funkcje-metody do ustawiania tych czasów, ale znacznie łatwiej ustawić je właśnie w pliku MD_UISwitch.h według szkicu 10. Proponuję, żebyś otworzył w edytorze (ja używam Notepad++) plik MD_UISwitch.h i zmieniał ustawienia tych czasów.

Szkic 10:

(…) /* // poniżej oryginalne wartości domyslne:
const uint16_t KEY_PRESS_TIME = 150;      ///< Default key press time in milliseconds
const uint16_t KEY_DPRESS_TIME = 250;    ///< Default double press time between presses in milliseconds
const uint16_t KEY_LONGPRESS_TIME = 600; ///< Default long press detection time in milliseconds
const uint16_t KEY_REPEAT_TIME = 300;   ///< Default time between repeats in in milliseconds
const uint8_t  KEY_ACTIVE_STATE = LOW; ///< Default key is active low – transition high to low detection */

// nowe wartości czasów w milisekundach:
const uint16_t KEY_PRESS_TIME = 300;     ///< maksymalny czas zwykłego = minimalny czas długiego naciśnięcia
const uint16_t KEY_DPRESS_TIME = 350;    ///< maksymalny czas pojawienia się „dwukliku” (nie za długi)
const uint16_t KEY_LONGPRESS_TIME = 1200; ///< maksymalny czas długiego naciśnięcia = moment interpretacji
const uint16_t KEY_REPEAT_TIME = 300;   ///< czas między powtórzeniami przy ciągłym naciskaniu klawisza
const uint8_t  KEY_ACTIVE_STATE = LOW;   ///< naciśnięcie = stan niski na pinie Arduino   (…)

Zmień długości czasów, zapisz plik nagłówkowy (a nie plik szkicu) MD_UISwitch.h, a potem od nowa skompiluj szkic A2005.ino z tymi nowymi czasami i obserwuj, jak są interpretowane naciśnięcia przycisku (lub kilku przycisków naraz).

Mamy cztery przypadki (i wyniki):

– krótkie naciśnięcie,

– długie naciśnięcie,

– „dwuklik”, czyli szybkie dwukrotne naciśnięcie,

– bardzo długie naciskanie, które w efekcie daje ciąg „impulsów” – wyników przez cały czas naciskania.

Według moich ustaleń czas KEY_PRESS_TIME to granica między zwykłym i długim naciśnięciem. Czas KEY_LONGPRESS_TIME to czas, po którym następuje interpretacja i uzyskanie wyniku także przy ciągłym naciskaniu klawisza. Przy naciśnięciach krótszych od czasu KEY_LONGPRESS_TIME, zwolnienie klawisza powoduje zinterpretowanie pomiaru (krótkie lub długie naciśnięcie) i wyświetlenie na konsoli monitora).

Gdy w czasie KEY_DPRESS_TIME po naciśnięciu przycisku nastąpi jego ponowne naciśniecie, wtedy wynikiem interpretacji będzie „dwuklik”. A jeżeli dwa kliknięcia pojawią się w odległości czasowej większej niż KEY_DPRESS_TIME, wtedy wynikiem interpretacji będą dwa naciśnięcia: zwykłe lub długie, zależnie od ustawień KEY_PRESS_TIME i KEY_LONGPRESS_TIME.

Gdy przycisk jest długo (ciągle, stale) naciskany, to po czasie KEY_LONGPRESS_TIME następuje interpretacja. Wynikiem interpretacji jest „normalne naciśnięcie” (a nie długie!), a potem co czas KEY_REPEAT_TIME pojawiają się kolejne „normalne naciśnięcia”. Można to nieco zmienić: jeśli w pliku …/src/MD_UISwitch.cpp zmienimy ustawienie:

enableRepeatResult(false)  na
enableRepeatResult(true)

wtedy po czasie KEY_LONGPRESS_TIME też pojawi się wynik „pojedyncze naciśnięcie ”, ale następne występujące co KEY_REPEAT_TIME impulsy – wyniki nie będą „zwykłymi naciśnięciami ”, tylko „autopowtarzaniem”, co czasem może być przydatne.

We własnym zakresie zmodyfikuj i skompiluj przykładowy szkic MD_UISwitch_Example.ino. Masz tam warunkową kompilację i do naszego zestawu 6 przycisków (digital array) musisz zmodyfikować dwie linie:

#define TEST_DIGITAL_SIMPLE 0
#define TEST_DIGITAL_ARRAY 1
#define TEST_ANALOG  0
#define TEST_MATRIX_4b4  0
#define TEST_MATRIX_1b6  0
#define TEST_MATRIX_4017KM 0

i dalej w szkicu dodać numery pinów, do których dołączone są przyciski. Program testowania zestawu „digital array” działa podobnie jak A2005.ino, tylko wyświetla na konsoli inne napisy.

Zależnie od potrzeb można wykorzystać wszystkie albo tylko niektóre z tych czterech możliwości (krótkie, długie, dwuklik i ciągłe), które domyślnie są włączone i dostosować  do tego ustawienia wspomnianych czasów.

W omawianym szkicu znajdziesz trzy następujące zakomentowane linie:

(…)
S.begin();
  //S.enableDoublePress(false);
  //S.enableLongPress(false);
  //S.enableRepeat(false);
  S.enableRepeatResult(true);
(…)

Domyślnie dwuklik (DoublePress), długie naciśnięcie (LongPress) i powtarzanie (Repeat) są włączone. Odkomentowanie ich (z atrybutem false) wyłączy je.

Jeżeli z kolei w ostatniej linii zmienisz true na false, ciągłe naciskanie da wyniki „normalne naciśniecie”, a nie „autopowtarzanie”.

Poćwicz! A w następnym odcinku (UR020c) wreszcie przechodzimy do biblioteki Adafruit GFX.

Piotr Górecki