LED matrix MAX7219 IC-vel

Tartalom:

  • A MAX7219 működése, műszaki adatok, felépítése
  • Letölthető programkönyvtár függvényei és működésük
  • A chip elemi szintű protokollja, vezérlés programkönyvtárak nélkül
  • Saját kijelző meghajtó függvények
  • Gyakorlati tapasztalatok a használat során, rossz földelés miatti hibák

—————————————————————————-

A MAX7219 LED meghajtó hasznos eszköz, ha egyszerre sok LED-et kell vezérelni, mert kevesebb chip kivezetést kell elhasználnunk a LED-ekre. Összesen 8×8=64 ledet vezérelhetünk. A Max7219-et kétféle eszközben használják. 8×8-as LED mátrixkijelzőben, és maximum 8 digites 7 szegmenses kijelzőkben. Az alábbi ismertetőben összeszedtem az infókat egy LedControl nevű könyvtár függvényeinek használatához, de a legvégén találsz egy általam megírt saját függvényre is példát, ami akkor hasznos, ha kevés már a memória, és ezért kevésbé univerzális programra van szükséged, mint a LedControl.

A MAX7219 a ledeket időszeletekben villogtatja, vagyis egyszerre mindig csak 8 világíthat, a többi éppen sötét. A 8-as led csoportokat kb 800Hz frekvenciával váltogatja, így szemünk úgy érzékeli, mintha mind folyamatosan világítana, de természetesen kicsit halványabban. Ha a kijelzőkre sokszor süt rá a nap, akkor ez nagy hátrány lehet. Ekkor hasznosabb lehet inkább 8db 74HC595 léptetőregiszter, az képes folyamatosan áramot adni egy-egy lednek! Az általam használt LED mátrix kijelző esetén azonban erről szó sem lehet, mert a LED-eket összeépítették egy tokba, és sorokba, illetve oszlopokba rendezték. Kizárólag időszeleteléses módon lehet használni. Így néz ki a kapcsolási rajza egy led mátrix kijelzőnek:

A MAX7219 bekötése és vezérlése nagyon egyszerű. Összesen három kivezetést használunk el a vezérlő kimeneteiből. Az egyes modulok felfűzhetők egymás után, és egyik a másiknak adja át a beléptetett adatokat.

A MAX7219 (vezérlő) kivezetései

  1. Data – Ezen keresztül léptetjük be az adatokat. Az összes led állapotának megváltoztatásához 8×2 byte adatot kell a chipbe küldenünk. A ledek 8-as csoportokban egyedileg írhatók 2 byte adattal.
  2. CLK – órajel bemenet. Minden órajel bemenetre adott felfutó él beolvassa a Data bemeneten lévő jelszintet, és belépteti a chip adat regiszterébe. A 2byte beküldése tehát 16 órajelciklus.
  3. Chip select – Aktív alacsony szint. Amikor erre a bemenetre alacsony szint kerül, akkor lehet az órajel felfutó éleivel adatokat írni a chip-be, illetve a sorban felfűzött chip-ekbe.

Műszaki adatok

ParaméterMAX7219
Tápfesz:4,0 V ~ 5,5 V
Tápáram:330mA
A szegmens meghajtó árama40mA
Szegmens frissítés500–1300 Hz (800 Hz tipikus)
órajellegfeljebb 10MHz

MAX7219 adatlap: http://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf 

Fényerőt is szabályozhatunk. Összesen 16 fokozatban adható meg a LED-ek fényereje egy minimum és maximum között. A fényerőt ekkor PWM módon szabályozza, azaz 0 és maximális áram folyik át a ledeken, és a kitöltési tényező változik, amikor a led egyébként világítania kell. Ez a szoftveres lehetőség. Azonban a chip egyik kivezetésére kötött ellenállás meghatározza a ledeken átfolyó áram nagyságát, így ezzel is lehet fényerőt szabályozni. Ezt az ellenállást a kijelző modulokra ráépítik, gyakorlatban nem kell vele foglalkozni! Azért itt egy táblázat annak, aki ennek cseréjére kényszerül valamilyen rejtélyes okból:

A táblázatban szerelő értékek Kohm-ban lettek megadva. Egy átlagos led esetén a maximális 40mA meghajtóáramhoz kb 10Kohm ellenállás szükséges, ezzel nem lehet nagyot tévedni.

A MAX7219 belső felépítése

A MAX7219 bemeneti interfésze egy 16 bites léptetőregiszter. Az első 8 bit meghatároz egy címet, a második 8 bit pedig maga az adat, amit a címmel meghatározott belső memóriába be kell írni (lásd később):

Több MAX7219 eszközt felfűzhetünk egymásra. A Din bemenetre mindig az előző chip Dout kivezetését kell kötni. Ez elsőre természetesen az Arduino valamelyik kimenetét. A CLK és Chip Select bemeneteket párhuzamosan kötjük az összes chipre.

Az biztosan nyilvánvaló, hogy ennek az IC-nek a használatához is van programcsomag. A szokásos helyen, a eszközök/könyvtárak kezelése menüpontban keressünk rá a “ledcontrol” szóra:

Mint a képen látható, és két programcsomagot is letöltöttem magamnak. Valójában csak a “LedControl” programcsomagot használom, de a “LEDMatrixDriver” nevű csomagot is megnézegettem. Utóbbiban inkább animációk, mozgó feliratok kezelését megkönnyítő programokat lehet találni. Nekem ez még nem kellett, elég volt néhány statikus felirat és ábra.

Az Arduino Ledcontrol könyvtár használata

A könyvtár legfeljebb nyolc láncra fűzött MAX7219 IC használatát támogatja.
A könyvtár inicializálásának tipikus kódja így néz ki:

#include “LedControl.h”
LedControl lc1=LedControl(12,11,10,1);

Ezzel a két sorral készen is vagyunk. Létrehoztuk az lc1 nevű változót, amivel a továbbiakban dolgozni fogunk a programban. Az inicializálás során 4 paramétert kellett megadni. Az első 3 paraméter az Arduino kivezetéseinek számai, amelyek a MAX7219-hez kapcsolódnak. Ezek lehetnek az Arduino tetszőleges digitális IO-érintkezői.  Nem kell inicializálni a megadott kivezetéseket kimenetekként, vagy beállítani azokat egy bizonyos állapotba, ezt a LedControl könyvtár megteszi. Az egyes megadott kivezetések szerepe:

LedControl(dataPin, clockPin, csPin, numDevices)

A negyedik paraméter sorba fűzött MAX7219 chip-ek esetén a lánc mérete. A könyvtár maximum 8 eszközt képes kezelni egyetlen LedControl változóból, így itt csak 0..7 közötti értékek megengedettek.

Ha a programnak 8-nál több MAX7219-et kell vezérelnie, akkor egy másik -változót kell létrehozni, amely 3 másik kivezetést használ:

#include “LedControl.h”
LedControl lc1=LedControl(12,11,10,8);
// első 8 eszköz… 
LedControl lc2=LedControl(9,8,7,8); // második 8 eszköz…  

Energiatakarékos üzemmód

A ledek elég sok energiát fogyasztanak, amikor világítanak. Előfordulhatnak olyan felhasználások, amikor ez számít a MAX7219 támogatja az energia takarékos kikapcsolási módot.

Ebben a módban a készülék a chip kikapcsolja az összes LED-et a kijelzőn, de az adatok megmaradnak. Amikor a chip-et vissza kapcsoljuk, ugyanazok a LED-ek világítanak, mint az alváskor. Lehetőség van az adatok frissítésére energia takarékos módban is, hiszen a chip egyéb részei működnek továbbra is. Ilyen esetben a chip felébresztésekor már az új adatok fognak látszani. Az energia takarékos mód ki és bekapcsolására az alábbi kód szolgál:

shutdown(int addr, bool status)

ahol az “addr” a modulnak a száma, tehát ha minden egyes MAX7219 IC-t energia takarékos módba akarunk küldeni, akkor annyiszor kell meghívnunk ezt a függvényt, ahány IC-t kapcsoltunk sorba. A visszakapcsolásra természetesen ugyanez vonatkozik! A “status” egy logikai érték, 1 (true) esetén bekapcsolódik az energia takarékos mód, míg 0 (false) esetén visszakacsolódik a led-ek fénye. Konkrét példa:

lc1.shutdown(0,true); //kikapcsoljuk a led-eket, energia takarékos mód
lc1.shutdown(0,false); //visszakapcsoljuk a led-ek fényét

Találtam az egyik leírásban egy megjegyzést arra vonatkozóan, hogy az energia takarékos mód a kijelző inicializálásakor nem biztos, hogy kikapcsolt állapotba kerül. Volt egy kijelzőm, aminek a hibás működésben lehetséges, hogy ez játszott szerepet. A hiba az volt, hogy áramszünet után a bekapcsolódást követően a matrix kijelző sötét maradt. Ekkor mindig a készülékhez kellett menni, és az Arduino reset gombjával kellett elindítani a készüléket, és így már működtek a kijelzők. Később az eszközt szétszedtem és újat építettem (nem az említett hiba miatt). Már nincs meg az áramkör, így nem tudom kipróbálni, de valószínűleg segített volna, ha a programba a kijelző inicializálást követően,kikapcsoltam volna az energia takarékos módot.

A kijelző fényerő beállításának lehetőségei

Három tényező határozza meg a képernyő fényerejét.

  • a kijelző szegmens áramát beállító RSET ellenállás értéke, amiről már előbbiekben volt szó
  • a MAX7219 ugyanazon pillanatban megvilágított led csoportjainak a száma. Alapértelmezetten ez 8 led csoport, ami egy mátrix kijelző esetén egyértelmű. Ha azonban 7 szegmenses kijelzőket hajtunk meg a MAX7219-el, akkor nem feltétlenül használunk 8 számjegyet. Ha pl. csak 4 számjegyünk van, akkor hosszabb ideig tudja kivilágítani a használt digiteket a chip. A mátrix kijelző esetén ez nem lehetőség, de megemlítem. Ha valakinek szüksége lenne rá, akkor a “scanlimit” szavakkal keresgéljen az IC adatlapjában. A LedControl könyvtárnak is van erre funkciója, de nem foglalkoztam vele.
  • egy fényerő beállító függvény, amely lehetővé teszi a LED-ek fényerejének a szoftverből történő vezérlését.

Az RSET áram beállító ellenálláshoz egy mátrix kijelző modul esetében nagyon nehezen férünk hozzá (smd ellenállás, nagyon nehéz kicserélni), ezért ezzel nem foglalkoztam. A szoftveres beállítás viszont igen hasznos. Érdemes a környezete megvilágítását pl. egy fototranzisztorral ellenőrizi, és hozzáigazítani a kijelző fényerejét. Ha a fototranzisztor működése érdekel, kattints ide a leírás elolvasásához! A fényerő beállítása 16 fokozatban történik, a függvény így néz ki:
setIntensity(int addr, int intensity);
ahol az “addr” a sorba kötött mátrix kijelző modulok közül a kiválasztott modul sorszáma. Vagyis minden egyes modulra meg kell hívni a függvényt. Az “intensity” a fényerő numerikus értéke. 0 a legkisebb, míg a 15 a maximális fényerő. Ha 15-nél nagyobb értéket adunk meg, akkor a maximális fényerőt állítja be a függvény. Konkrét példa közepes fényerő beállításra:
setIntensity(0,8);

A kijelző törlése

A függvény neve:
clearDisplay(addr);
ahol az “addr” a modul sorszáma. Működése egyértelmű, törli az összes led fényét, alaphelyzetet állít be a kijelzőn. Nem azonos a shutdown függvénnyel, ami szintén kikapcsolja az összes ledet, de nem törli az információt a chip memóriájából.

Egyetlen LED ki és bekapcsolása

A függvény neve:
setLed(int addr, int row, int col, boolean state);
ahol az “addr” a modul sorszáma, “row” a sor index, “col” az oszlop index, “state” pedig az állapot, amit szeretnénk (kikapcsolásnál 0, bekapcsolásnál 1).

A mátrixban 8 sor van (indexelve 0..7-től) és 8 oszlop (szintén indexelve 0..7-től). Ha fel szeretnénk kapcsolni egy ledet a felülről a második sor jobb oldalán található harmadik LED-et, akkor a LED indexét kell használni sor- és oszlop argumentumként:
setLed(0,1,2,true);
A paraméterek értelmezésénél vegyük észre, hogy a második sor indexe 1, mivel az indexek 0-val indulnak. A harmadik led indexe a sorban ennek megfelelően 2. Természetesen a matrix kijelző vezetékezésének ilyennek kell lennie:

Sor ledjeinek vezérlése

Az egy sorban található ledeket egyetlen függvénnyel is lehet kapcsolni. Ennek a függvényenek a neve:
setRow(addr, row, value);
ahol az “addr” a modul sorszáma, “row” a sor index (felülről számolva), “value” pedig az állapot, amit szeretnénk ez egyes ledeken látni. Az utolsó paraméter egy byte, aminek a bitjei megmondják az egyes led-ek állapotát (kikapcsolásnál 0, bekapcsolásnál 1). Az egyes biteket megadhatjuk binárisan pl. lc.setRow(0,7,B10110000) vagy ha ez nem lehetséges, akkor decimális értékben is. Utóbbi esetben hasznos az alábbi táblázat:

Bit-érték1286432168421
Led On?10110000
Sor-érték128032160020

A táblázatban a “Led On” feliratú sorban 1-el jelezték, ha a led be van kapcsolva. A konkrét példában a függvény value értéke 176 (128+32+16), és ekkor balról a az első, harmadik és negyedik led világít.


A setRow() függvénnyel nyilvánvalóan sokkal gyorsabban kiírható egy kép mit a setLed()-el, mert egy sor tartalmát utóbbi függvénnyel nyolc hívással lehetne kiírni. Ráadásul azt is tudjuk, hogy a MAX7219-ben egy 8×8-as regiszter található, melyet byte-onként lehet írni. A regiszter szervezése olyan, hogy egy byte egy sor adatait tartalmazza, és amikor adatokat írunk a MAX7219-re, akkor egyszerre egy sor adatait adjuk át. A következő függvény a setColumn() ami egy oszlopot tud beállítani, szintén nyolc írási ciklussal tudja frissíteni az oszlopban található led-ek állapotát. Ezért ha csak lehetséges a setRow() függvényt használjuk.

Oszlop ledjeinek vezérlése

Már említettem az előbb a függvényt:
setColumn(addr,column,value);
Magyarázatra az előbbiek alapján már nem szorul a működés és a paraméterek. Az egyes ledeket itt is bináris számmal lehet a legvizuálisabban kijelölni, de ha erre nincs lehetőség, az előző táblázat itt is működik.
Megosztanék egy tapasztalatot. Amikor a matrix kijelzőt először használtam, véletlenszerűen ezzel az oszlop vezérlő függvénnyel írtam ki a karaktereket. Nem vettem észre, hogy az adatok átküldése így sokkal tovább tart. Kicsit később még részletesen olvashatsz a részletekről. Én igen sokat tanultam az esetből. Amikor ezt a leírást készítettem, felfigyeltem arra, hogy az oszlop vezérlő függvény nem optimális, rengeteg adatot mozgat. Átírtam tehát a sor vezérlő függvényre. Megérte, mert észrevehetően gyorsabb lett a karakterképek váltása!

7 segmens kijelző vezérlése

Nyilvánvalóan nem csak mátrix kijelzőt köthetünk a chip-re. Összesen 8 db 7 szegmenses kijelzőt is tud vezérelni:

Ehhez is találunk függvényt:
setDigit(addr, digit, value, dp);
A paraméterekben az “addr” a modul sorszáma, a “digit” a 8 kijelző egyikének indexe, a “value” a kijelezni kívánt érték, a “dp” pedig a tizedespont. A tizedespontot true vagy false értékekkel vezérelhetjük. Pl a következő programrészlet egy háromjegyű számot jelenít meg:
byte egyes=2; 
byte tizes=3;
byte szazas=4;
lc.setDigit(0,2,szazas,false);
lc.setDigit(0,1,tizes,false);
lc.setDigit(0,0,egyes,false);

A program futásának eredménye a 432 szám kijelzése a kijelzőn. Nem próbáltam ki, mert nem használtam még 7 szegmenses kijelzőt a MAX7219-el, de biztosan működik. Nyilván a chip nem tudja, hogy mátrix vagy 7 szegmens kijelzők lógnak rajta, így a számjegyek törlésére a clearDisplay(addr); függvényt használhatjuk, illetve pár sorral lejjebb találunk a számjegyenként történő törlésre is példát.

Korlátozottan betűket is meg tud jeleníteni egy 7 szegmenses kijelző. Ezért csináltak egy másik függvényt is:
setChar(addr, digit, value, dp);
A működését nem kell magyarázni az előző függvény alapján, de kérdés, hogy mit lehet a value értékhez írni? Hát ezt:
0 1 2 3 4 5 6 7 8 9
A a (nagybetűt jelenít meg)
B b (kisbetűt jelenít meg)
C c (kisbetűt jelenít meg)
D d (kisbetűt jelenít meg)
E e (nagybetűt jelenít meg)
F f (nagybetűt jelenít meg)
H h (nagybetűt jelenít meg)
L l (nagybetűt jelenít meg)
P p (nagybetűt jelenít meg)
– (mínuszjel)
. , (kigyullad a tizedes pontot)
(aláhúzás)
<space> (üres vagy szóköz)

Saját kezelőfüggvény

Nemrég arra kényszerültem, hogy még alaposabban megismerjem a MAX7219 led vezérlő IC-t. Már fentebb is említettem, hogy gondjaim voltak. Az történt, hogy a kezdetben jól működő időjárás állomásom led mátrix kijelzői idővel vacakolni kezdtek. Már kezdettől fogva bajom volt velük. Az első időjárás állomás verzióm amit megépítettem, csak egyetlen hibát produkált, nevezetesen azt, hogy tápfesz bekapcsoláskor (vagy áramszünet után) nem működött a led mátrix kijelzők egyike sem. Tápfesz bekapcsoláskor minden sötét maradt. Ha azonban reset gombot megnyomtam az Arduino-n, akkor már rendben működni kezdett minden kijelző. Mivel évente egy-két áramszünetünk volt akkoriban, nem okozott nagy lelki konfliktust és nem törődtem a hibajelenséggel. Telt múlt az idő, és építettem egy új időjárás állomást, kissé átalakított kijelzővel, erre már csak 3 led matrix panel került. Mivel már volt tapasztalatom, kapásból a véglegesnek szánt panelt építettem meg, amire kb. 2 m kábelt szereltem, ez kötötte össze a kijelzőket az Arduino-val. Ennek a tápfesz bekapcsolásakor speciel semmi baja nem volt, azonban pár perc működés után zagyvaságokat kezdett kijelezni, aztán teljesen le is fagyott a kijelzés. Tápfeszre gyanakodtam, és valóban egy extra 100 mikroF szűrőkondi segített. Megnyugodtam, hogy megtaláltam a hibát! Azonban az új időjárás állomás a végleges helyére történő felszerelés után, igen sok problémát generált, szinte semmi nem működött, pedig a próbapanelen még rendben volt. Kezdeti néhány hónapos működés után a led mátrix kijelző megint elkezdett kriksz-kraszokat kijelezni. Bosszankodva szétszedtem, és nagy duzzogva beforrasztottam még egy szűrőkondit. És ismét működött. Aztán kis idő múlva elindult újra a folyamat. Már gyanítottam, hogy nem a szűrőkondi a megoldás. Vártam, hogy mi történik. És történt is valami! Időnként magától helyreállt a kijelzés. Kis idő múlva megint kriksz-karaksz, aztán megint jó. Egyszer aztán teljesen elsötétültek a kijelzők, és immár semmit nem jeleztek ki. Ha ekkor reset-et nyomtam, esetleg megjavult néhány percre, vagy kapásból krisz-kraksz, és megint sötétség. Érdekes módon olyan eset is előfordult, hogy a három sorba kötött kijelzőből a legelső (amire az Arduino kimeneteit kötöttem) rendben működött, de a többi már sötét maradt. Elkezdtem valami másra gyanakodni. Az Arduino-ba betöltött programban a ledmatrix könyvtárat használtam. Belenéztem a forrásába, és azt láttam, hogy minden időzítés nélkül küldi az adatokat, ami csövön kifér. Talán az a baj, hogy a 2 m vezeték sok zavart szed össze? Ahhoz, hogy ez kiderüljön, elkezdtem egy saját ledmatrix függvényt írni, amibe késleltetéseket építettem be, és így csökkentettem a CLK frekvenciáját. Azért írtam meg inkább, mert a letöltött könyvtár programja túl bonyolult volt, hogy biztonsággal belejavítsak. Ráadásul a github-ról letöltött függvénykönyvtár túl sokat tud számomra, nekem nem kellett ennyire univerzális eszköz. Az időjárás állomás programom egyébként is olyan nagy volt már, hogy alig fért a memóriába, és a változóknak rendelkezésre álló memória is alig volt már szabadon. A fordításkor az Arduino IDE ki is írta, hogy kevés a szabad memória, stabilitási gondok lehetnek. Akár ez is okozhatta a jelenséget, erre is utaltak nyomok. Átalakítottam a DS18B20 hőmérők kezelését, ami felszabadított egy kicsi memóriát, és néhány hétig megint rendben volt minden. Azt elfelejtettem megemlíteni, hogy a DS18B20 hőmérők egyike szintén vacakolt, állandóan paritás hibát jelzett a kiolvasás, de a négyből mindig csak az egyik. Ezzel a memória növelő program átalakítással a hűmérők meggyógyultak. A kijelzők is megjavultak, de aztán megint elromlottak. Mással próbálkoztam, elkezdtem a programban valamilyen kijelző írással kapcsolatos hibát keresni. Kezdetben a ledmatrix könyvtárnak azt a függvényét használtam, ami oszlopokat írt, és nem sorokat. Nem vettem észre, hogy így sokkal több adatot kommunikál a kijelző felé a program, hiszen egy sor írása nem egy, hanem 8 külön 16bites írási ciklussal csinálható meg. Amikor ezt kijavítottam, a kijelző hirtelen életre kelt, és néhány hétig hibamentesen működött. Ezt annak tudtam be, hogy kevesebb kommunikáció zajlott, és az esetleges vezeték zavarok ritkábban zavarták meg az adatátvitelt. Egyszerűen érthetetlen számomra, hogy mitől romlott el ismét. Esetleg a párt tehet róla, vagy keveset adakoztam a templomban?

Mindegy, bármi is a baj, segíthet, ha optimálisabb a program, több a változóknak fenntartott memória, és ezzel együtt lelassítom a kommunikációt, hogy a 2m vezeték-ben esetleg megjelenő zavarok ne legyenek hatással. Az elképzelés az, hogy amikor egy adatot beállítok a MAX7219 bemenetén, akkor jó sokáig várok, (mondjuk  10-50mikrosec-ig) és csak ekkor kapcsolok felfutó élet a CLK bemenetre. Várhatóan az eltelt időben a hosszú vezetéken lezajlanak a tranziens jelenségek, beáll az adat bemeneten a jelszint, és nem fog váratlan értéket beolvasni a MAX7219.

Ahhoz, hogy megírjam a saját vezérlő programomat, meg kellett érteni a MAX7219 írásnak protokollját. Tanulmányozni kezdtem tehát az adatlapot. Szeretném is közreadni azt, amit megfejtettem belőle.

Lássuk először az adatok írásának idő diagrammját. Ami ebből számomra lényeges volt, hogy a CS bemenetre kapcsolt 0 szint (engedélyezés) után, várni kell egy kis időt amíg mehet az első órajel felfutó éle (t CSS, nyíl mutatja). Azt régebbi tapasztalatamból tudom, hogy itt nanosec időkről van szó, így ide biztosan jó lesz néhány mikrosec várakozás. A másik lényeges információ, hogy az adatbemeneten lévő jelszint beléptetése az órajel bemenet felfutó élénél történik meg, és jelzi is az ábra, hogy itt is kell egy kis várakozási időt hagyni (t DS, nyíl mutatja), tehát az adat bemenetre előbb kell odarakni az adatnak megfelelő jelszintet, mint ahogy megjelenik a felfutó él. Ebben semmi meglepő nincs szövegesen is leírták az adatlapban.

Most következzék az a rész, hogy milyen adatokat is léptethetünk be az adatbemeneten a chip-be. A kijelzőnek egy írással 16 bit információt kell átadni. Ebből 8 bit megcímzi a chip adatregiszterét, a következő 8 bit pedig maga az adat, amit a megcímzett regiszterbe beleírunk. Így néz ki egy írási bitsorozat:

Ahhoz, hogy a chip megjelenítse a 64 led ponton az általunk megkívánt információt, 8db 8 bites adatregiszterbe kell beírni soronként az információt. Minden sorban az 1 esetén ég a led, 0 esetén meg nem. Ezek az adatregiszterek. Van ezen kívül még néhány vezérlő regiszter, amivel a működést lehet befolyásolni, fényerőt állítani (PWM módon, de azt maga a MAX7219 IC csinálja a megfelelő regiszter tartalma alapján), kikapcsolni a kijelzőt energiatakarékos módba stb. Alább az egyes regiszterek felsorolása. Ebben a táblázatban rögtön azt is láthatjuk, hogy milyen bináris értéket kell az ADRESS bitekben megadnunk, hogy a második 8 bit adata a megfelelő regiszterbe íródjon. Látszik, hogy nem spóroltak a bitekkel, négyet eleve fel sem használtak a regiszter címzéshez. Feltételezem, hogy valamilyen meglévő chip struktúráját használták fel, és nem volt érdeme áttervezni azért, hogy 4 bittel kevesebbet kelljen írni, mert ugye annyi is pont elég lenne. Lehet, hogy más szempontok is voltak, de tény, hogy nincs szükség 8 bit címre a 15 db adat és státus regiszter eléréséhez. Arról nem is beszélve, hogy a status regiszterek értékes adattartalma esetenként egyetlen bit (pl. shutdown regiszter).

Lássuk is sorban status regiszterek működését.

Shutdown regiszter:

Tehát a shutdown regiszterbe 1-et kell írni, ha azt akarjuk, hogy megjelenlenek a beírt információk a kijelzőn. Ha 0-at allítunk be, akkor a chip működik tovább, lehet bele adatokat írni, mindössze lekapcsolja a led-eket, így az áramfelvétel drasztikusan csökken. Ugyanezt érjük el azzal is, ha az adatregiszterek mindegyikébe 0-át írunk. Így lehet a kijelzőt törölni. A shutdown értelemszerűen nem törli a kijelzőt, csak kikapcsolja.

Fényerő regiszter:

Erről nem kell sokat regélni. 16 fokozatban lehet fényerőt állítani, amit a led áramának kapcsolgatásával, illetve a led áram kitöltési tényezőjének állítgatásával ér el a chip. A 0 érték a minimális fényerő. Itt már világit.

Decode mód regiszter:

Ez a regiszter akkor használatos, ha nem mátrix panelt kötünk a MAX7219 kimeneteire, hanem maximum 8db 7 szegmenses led számkijelzőt. Ekkor nem kell szenvednünk azzal, hogy kiszámoljuk melyik számjegy kijelzéshez melyik biteket kell az adatregiszterekben 1-re állítani. Megteszi helyettünk a chip, beírjuk az adatregiszterbe a számot, amit szeretnénk látni, chip pedig megjeleníti a kijelzőn. Persze ehhez a kijelző kivezetéseit jól kell bekötni. Ezzel azonban nem foglalkoztam, így ezt fejtse meg az akinek szüksége van rá. Minden esetre nekem ebbe a regiszterbe 0-át kellett írni, mert nem kell dekódolás a mátrix kijelzőhöz. Úgy tapasztaltam, ez a gyári alapbeállítás, ha tápot adok a chip-nek akkor erre áll be a regiszter tartalom.

Scan-Limit Register:

Ennek a regiszternek szintén csak akkor van jelentősége, ha 7 szegmenses számjegyek meghajtására használjuk a chip-et. Ha 8db 7 seg ledet kapcsolunk rá, akkor mindegyik csak 1/8-ad ideig világít. Emiatt a kijelzők fényereje lényegesen kisebb, mintha külön-külön mindet folyamatosan árammal hajtanánk meg. Persze pontez ennek a chip-nek a lényege, hogy nem kell minden led meghajtáshoz külön kivezetés. Azonban ha kevesebb digitet használunk fel, pl. egy órában csak 4 számjegy kell, beállíthatjuk a számjegyek számát, és növekedni fog a fényerő. Ennek oka, hogy a chip nem fog foglalkozni a fennmaradó számjegyeknek megfelelő sorokkal, azokat ki fogja hagyni a megjelenítés ciklusából, és így hosszabb ideig fognak világítani a fennmaradó digitek. Bocsi, de ezzel sem foglalkoztam, nincs is kéznél ennyi 7 seg kijelzőm, hogy kipróbáljam, de biztosan működik. Megjegyzem, az időjárás állomosomban pont azért használtam 74LH595 léptetőregisztert, mert az képes folyamatosan árammal meghajtani a led-et, így nagyobb a fényerő. A nagy fényerő meg azért kell, mert időnként rásüt a nap. Még max. folyamatos áramnál is alig látszik a kijelzett érték ilyenkor. Lakásban azonban tökéletes lenne egyetlen MAX7219 az összes 7 szegmenses kijelzőnek amit használtam.

És akkor következhet a saját ledmátrix vezérlő program. Igazán nem nagy durranás. Én úgy gondoltam, egyetlen függvényt írok, ami egy 16 bites adatot fog kiléptetni a három kivezetésre. A működés tök egyszerű: a függvénynek átadok két byte-ot, először az egyiket, aztán a másodikat léptetem ki az adat kivezetésre. Közben a fenti protokoll szerint kapcsolgatom a CS és CLK kivezetéseket. A léptetések közben a megfelelő pontokon a delayMikros() függvénnyel késleltetéseket építettem be. Egy karakterkép kiléptetése 8 db függvényhívás a 8 sornak megfelelően. Ha valamelyik státus regisztert kell írni az is egy-egy függvényhívás.

/***********************************
* Ledmatrix kivezetések bekötése:  *
* D10 - CS                         *
* D11 - CLK                        *
* D12 - DIN                        *
***********************************/
const byte adat=12;     //MAX7219 adat in bemenetének vezérlésére szolgáló Arduino kimenet száma 
const byte cs=10;       //MAX7219 CS bemenetének vezérlésére szolgáló Arduino kimenet száma
const byte clk=11;      //MAX7219 CLK bemenetének vezérlésére szolgáló Arduino kimenet száma
const byte idozites=50; //A késleltetetés ideje mikrosecundum-ban
long kezdoido;          //az időméréshez használt változó, amit most nem használok

void setup() {
//  Serial.begin(9600); //az időméréshez használt sor, amit most nem használok
  pinMode(adat,OUTPUT);
  pinMode(cs,OUTPUT);
  pinMode(clk,OUTPUT);
  digitalWrite(cs,1);   //a max7219 cs bemenete 1-en van, amikor nincs kiválasztva, és nem kommunikálunk vele
  ledmatrix(9,0);       //Decode mód kikapcsolása
  ledmatrix(11,7);      //Scan limit regiszter beállítása (8 számjegy kijelzését kapcsoljuk be)
  ledmatrix(10,0);      //Intenzitás minimum
  ledmatrix(12,1);      //Shutdown kikapcsolva
}

void loop() { 
//  kezdoido=micros();   //az időméréshez használt sor, amit most nem használok
  ledmatrix(1,85);
  ledmatrix(2,170);
  ledmatrix(3,85);
  ledmatrix(4,170);
  ledmatrix(5,3);
  ledmatrix(6,15);
  ledmatrix(7,63);
  ledmatrix(8,255);
  delay(1000);           //időmérés esetén ezt a sort kell kimmentezni
  ledmatrix(1,0);
  ledmatrix(2,0);
  ledmatrix(3,0);
  ledmatrix(4,0);
  ledmatrix(5,0);
  ledmatrix(6,0);
  ledmatrix(7,0);
  ledmatrix(8,0);
  delay(1000);           //időmérés esetén ezt a sort kell kimmentezni
//  Serial.println(micros()-kezdoido);  //az időméréshez használt sor, amit most nem használok
}


void ledmatrix(byte felsobyte, byte alsobyte) {
  digitalWrite(cs,0);                         //cs bemenetre 0-at kapcsolunk, kijelző kiválasztva, jöhetnek az adatok
  delayMicroseconds(idozites);                //késleltetés mielőtt bármit csinálunk
  for (byte i=0;i<8;i++) {                    //először a felsobyte adatot fogjuk kiléptetni bitenként jobbról balra
    digitalWrite(adat,(felsobyte<<i) & 128);  //Minden ciklusban eggyel többször léptetjük balra, és a baloldali bitet kimaszkoljuk
    delayMicroseconds(idozites);              //késleltetés (adat már az adatvezetéken van)
    digitalWrite(clk,1);                      //beállítunk a clk vezetéken eg felfutóélet, ennek hatására belép az adat a chip-be
    delayMicroseconds(idozites);              //késleltetés (adat még mindig az adatvezetéken, órajel felfutóéllel beléptetve)
    digitalWrite(clk,0);                      //levesszük az órajel vezetéken az 1-et, órajel lefutóél ezzel együtt változhat az adat is
  }
  for (byte i=0;i<8;i++) {                    //az alsóbyte-ot is kiléptetjük, ugyanaz mint előző ciklusnál, bitenként léptetünk
    digitalWrite(adat,(alsobyte<<i) & 128);
    delayMicroseconds(idozites);digitalWrite(clk,1);delayMicroseconds(idozites);digitalWrite(clk,0);delayMicroseconds(idozites);
  }
  digitalWrite(cs,1);       //cs bemenetet vissza állítjuk 0-ra, chip nincs kiválasztva, illetve ezzel a felfutó éllel jelenik meg a beléptetett adat a kijelzőn
}

A program magáért beszél. A setup() részben beállítom a státus regisztereket, a loop()-ban pedig másodperces ciklusban más-más karakterképet írok ki a kijelzőre. Fontos tanulság, hogy a scan limit regisztert be kell állítani, mert tápfesz ráadásakor nem alapértelmezett, hogy minden számjegyet megjelenítsen. Pedig ebben az üzemmódban nem is karaktereket jelenítünk meg, mivel decode mód nincs beállítva. Bár karaktereket attől még lehet megjeleníteni, mert nincs dekódolás!
Azért építettem fel így a demó programot, hogy lássam és mérhessem a késleltetések hatását. Pl. a ha 1milisec-re állítom az órajel váltások közötti időt, akkor már jól láthatóan soronként jelenik meg az infó a kijelzőn. Ez nem is csoda, hiszen egy órajel periódus 2msec, így egyetlen regiszter írása 16×2=32misec. Mivel 8 regisztert kell írni, ami 256msec, ami negyed másodperc. Kísérletezgetéssel azt állapítottam meg, hogy 50mikrosec tökéletesen megfelelő késleltetés, ekkor egy órajel periódus idő 100mikosec, vagyis kb. 10Khz. Méltán számíthatok arra, hogy ekkora késleltetési időkkel nem fog számítani a vezeték hossza. Egy karakterkép kiléptetésének ideje ekkor 18000mikrosec. Ezt nem kiszámoltam, hanem megmértem. A példa programban benne is hagytam a szükséges sorokat, csak kikommenteztem. A méréshez a micros() függvényt használtam. Lekérdeztem a loop() elején és végén az eltelt időt, miközben kétszer frissítettem a kijelző teljes tartalmát. A delay()-t természetesen kikommenteztem a mérés idejére. A soros porton kijelzett adat 36000mikrosec volt, de az két karakterkép. Ebből következően egy karakterkép frissítése 18milisec. Vagyis kb. 50 karakterkép váltást lehet csinálni 1 másodperc alatt, menne a mozgófilm.

Kíváncsiságból megírtam teljesen ugyanezt a funkcionalitást a ledmatrix könyvtár függvényeivel. Ebben ugyebár nincs semmi késleltetés.

/***************************************************
* Ledmatrix kivezetések bekötése:                  *
* D10 - CS                                         *
* D11 - CLK                                        *
* D12 - DIN                                        *
* LedControl(DIN,CLK,CS,sorbakötött modulok száma) *
****************************************************/
#include "LedControl.h"
LedControl lc=LedControl(12,11,10,1);
//long kezdoido;

void setup() {
  Serial.begin(9600);
  lc.shutdown(0,false);   //shutdown kikapcsolva
  lc.setIntensity(0,0);   //intenzitás minimum
}

void loop() { 
  //kezdoido=micros();
  lc.setRow(0,0,85);
  lc.setRow(0,1,170);
  lc.setRow(0,2,85);
  lc.setRow(0,3,170);
  lc.setRow(0,4,3);
  lc.setRow(0,5,15);
  lc.setRow(0,6,63);
  lc.setRow(0,7,255);
  delay(1000);
  lc.setRow(0,0,0);
  lc.setRow(0,1,0);
  lc.setRow(0,2,0);
  lc.setRow(0,3,0);
  lc.setRow(0,4,0);
  lc.setRow(0,5,0);
  lc.setRow(0,6,0);
  lc.setRow(0,7,0);
  delay(1000);
  //Serial.println(micros()-kezdoido);
}

Ugyanezzel a módszerrel megmértem a végrehajtási időt is. 3632mikrosec-nek adódott, ami ezt jelenti, hogy egy karakterkép kiírása 1,8milisec. Pont tízszer gyorsabb, mint az én függvényem. Majdnem elfelejtettem megemlíteni, hogy mindkét programban egy modult használtam, bár van jelenleg is 4 ledmatrix modulom. Kísérletezgetek még egy kicsit, mert ugyebár az időjárás állomásomon három modul van. De most elfogyott az időm. Folyt .köv.!

Rossz hírem van magamnak, a saját ledmatrix() függvény nem oldotta meg a problémámat. Kicseréltem az időjárás állomásban az Arduino nano-t egy olyan példányra, amiben a saját függvénnyel írogatom a kijelzőt. A hibajelenség gyakorlatilag ugyanaz maradt, a kijelzők nem működnek. Tehát valamilyen külső körülmény befolyásolja a működést. Közben egyre melyebben ástam bele magam a mátrix kijelző vezérélésébe, és eljutottam a shiftout() függvényhez, ami ténylegesen az adatok kijelzőre küldését végzi. Csináltam egy próba programot, hogy megmérjem enne a függvénynek működési idejét. Különösen az órajel kimeneten megjelenő frekvencia érdekelt. Írtam egy programot a méréshez:

//az időmérési kísérletet egy 74HC595 shift regiszterrel végeztem
//a 74HC595-re egy 7 szegmenses kijelzőt kötöttem

const byte SH_CP = 8;       //chip 11-es láb, órajel bemenet (felfutó élre lépteti be az adatot)
const byte ST_CP = 9;       //chip 12-es láb, Latch bemenet (beléptetett adat kimenetre írása felfutó éllel
const byte OE_negalt = 10;  //chip 13-as láb, kimenet engedélyező (pwm fényerő szabályozáshoz)
const byte DS = 11;         //chip 14-es láb, adatbemenet
long kezd;
long veg;

void setup()
{
  Serial.begin(9600);
  pinMode(7, OUTPUT);digitalWrite(7,LOW);delay(10);digitalWrite(7,HIGH);delay(10); //kap egy reset jelet a chip
  pinMode(SH_CP, OUTPUT);
  pinMode(ST_CP, OUTPUT); 
  pinMode(OE_negalt, OUTPUT);analogWrite(OE_negalt,253); //kis fényerő, hogy ne világítson a szemembe
  pinMode(DS, OUTPUT);
  kezd=micros();
  digitalWrite(ST_CP,0);                 //Latch bemenetre 0-at kapcsolunk, jöhetnek az adatok
  shiftOut(DS, SH_CP, MSBFIRST, 255);    //adatok kiléptetése, az adatvezetéket és az órajelet a shiftout() vezérli
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  shiftOut(DS, SH_CP, MSBFIRST, 255);
  digitalWrite(ST_CP,1);                 //Latch bemenetet vissza állítjuk 1-re, a beléptetett adtok megjelennek a 
                                         //shiftregiszter kimenetein. Csak egy shiftregiszterem volt, de sokkal több 
                                         //adatot léptettem ki a bemenetére, nyilván csak az utolsó 8 bit tárolódik
                                         //a 74HC595 chip-ben.
  veg=micros();
  Serial.println(veg-kezd);              //végrehajtási idő kiírása (Arduino UNO-n 988mikrosec)
}

void loop()
{ 
}

A program 10 shiftout() függvényhívást végez, és a végrehajtási idő (amit kiírok a végén a soros portra) 988 mikrosec. Mivel ez 10×8 bit írását jelenti, az órajel kimeneten 80 impulzus jelenik meg. Vagyis egy impulzus ideje kb. 988/80=12,35 mikrosec. Ez kb. 81kHz frekvenciának felel meg. 2m vezetéken ekkora frekvenciának át kell mennie érzésem szerint, tehát a saját függvénnyel ezért nem mentem semmire. Marad a tápegység, vagy valamilyen más külső körülmény, ami zavarja a működést.

Közben hideg lett, és egyszer csak elkezdett működni magától a kijelző. Két napig minden rendben volt. Aztán egy napra 14 fokra emelkedett a hőmérséklet, és ismét zagyvaságok jelentek meg a kijelzőn, kis idő múlva pedig újra teljesen sötétek a kijelzők. Talán a tápegység lehet a ludas, hamarosan kicserélem. Azért lehet más is. Eddig nem gondoltam rá, de a akár zavarjelek is okozhatják a problémát. Van egy 1,5KW-os naperőművünk itthon. Most vettem észre, hogy szerencsétlen módon az időjárás állomás vezérlő egységének dobozát a naperőmű invertere mellé szereltem. Ez ugyebár egy kapcsolóüzemű tápegység, ráadásul nem is kicsi teljesítménnyel. Télen nem sokat süt a nap, kicsi a teljesítmény, talán ezért működik újra a kijelző. A meleg napon a nap is sütött, és újra elromlott a kijelző. Nem meggyőző érv, mert éjszaka se működik az inverter, a kijelzők mégsem működnek.

Az utolsó sorok írása közben eltelt újabb néhány hét, és rávettem magam, hogy a nagy hideg ellenére tovább kísérletezzek. Szétszedtem a központi vezérlőt (amin a két Arduino nano található), és barkácsoltam rá egy plusz föld kivezetést. Így már nagyon egyszerűen egy külső tápegységről is tápot adhattam a kijelzőknek. Egy 2,5A-es USB telefontöltőt használtam. Természetesen nem működött, túl egyszerű lett volna. Nem volt mit tenni, szétszedtem a kijelző oldalt is. Már nem emlékeztem rá hogyan is lettek ott a dolgok bekötve, felmerült bennem a gyanú, hogy esetleg nem tökéletes a földelés, vagy valami más rendellenességet találok. Minden rendben volt látszólag, kimértem a föld és a jelvezetékeket, nem volt semmi megszakadva. Azonban felfigyeltem arra, hogy a mátrix kijelző táp és jelvezetékeit meglehetősen vékony vezetékből készítettem, és ráadásul a mátrix “lánc”-nak csak az egyik végét kötöttem be. Ez a kép mutatja azokat a szürke sorkábeleket, amiket használtam. A bal oldali kijelző a lánc vége:

Ebben az esetben azonban egyik mátrix a másiknak adja át a tápfeszt egy nagyon vékony vezetéken. Ezek a vezetékek nem hosszúak, de hátha mégis ez gondot okoz. Többé kevésbé jól hozzáférhető volt a “lánc” másik vége (bal oldali kijelző a képen), és egy vastagabb vezetékkel bekötöttem oda is a földet és a +5V-ot. …és csoda történt. Azóta minden rendben működik.

Mennyire volt hasznos amit olvastál?

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet!