Egy karácsonyi, két estés projekt került most ledokumentálásra. Alig pár nap alatt került lefejlesztésre a low-budget fadísz. Az igazi nehézséget az okozta, hogy már nem lehetett új alkatrészeket beszerezni.
Az ötlet egy csillag alakú NYÁK volt, amin 5 LED villog, egy-egy LED minden csúcsban. A megvalósítás során egy Attiny13A használatára kényszerültem, ez megkötötte a kezem, és programozási cseleket kellett használnom. A tápellátást egy CR2032-es elemmel valósítottam meg.
Az említett mikrokontroller mindössze 8 lábú, ha ebből levonjuk a két táplábat, és a programozáshoz szükséges RESET negált lábat, akkor 5 láb mard. Külső IC és charlieplexing használata nélkül az összes lábat felhasználtam. Mivel a tápellátás nem engedett meg magas órajelet, és nem több, mint 64 bájt memória használható, ezért charlieplexing nem jöhetett szóba fényerőszabályzás mellett. Végül a különböző animációk közti váltást, a bekapcsolást, és a kikapcsolást is a RESET negált bemenetre kötött gombbal oldottam meg. Ehhez egy inicializálatlan változót használtam. Az első lefuttatott utasítások között szerepel egy, amely megnöveli egyel a eme char típusú változó tartalmát. Amennyiben meghaladta a tárolt szám értéke az animációk számát, beállította az értéket nullára. A Power Down mód elérhető AVR processzorokban utasításból, létrehoztam egy power_down animációt. Mivel a RESET interrupt felkelti az AVR-t Power Down módból, ezért a bekapcsolás is megoldottá vált ugyanazon gomb felhasználásával.
Ezen trükk lekódolásához szükség volt egy inicializálatlan változóra, ez a feladat a vártnál nehezebbnek bizonyult. Mint kiderült, az avr-gcc minden inicializálatlan változót úgy tekint, mintha 0-val lenne inicializálva minden indításkor.
Megjegyzés: külső IC (pl. shift regiszter) hely hiányában nem volt használható a tervezés során.
A 64 bájt RAM és az 1 KB Flash memória a struktúra megtervezése során gondolkodásra késztetett. Számos változót regiszterekben tároltam. Ez megkötötte a kezem. Többnyire char változókat kellett alkalmaznom, azonban ez közel se jelentett megszorítást. Ahhoz, hogy egy változót regiszterben tároljunk csupán néhány dologra van szükség: register kulcsszóval el kell látni a változót; asm() fordító direktívába meg kell fogalmazni, hogy melyik regiszterben kívánjuk tárolni a változó; a változónak globálisnak kell lennie; az avr-gcc-nek –ffixed-rxx paramétert kell adni, hogy ne használja a fordítás közben az adott regisztert; végül a regiszter méretének legalább olyan szélesnek kell lennie, mint a kívánt változónak. Tipikus felhasználás például egy 8 bites ciklusváltozó, melynek fölösleges memóriában tárolódnia.
A software PWM kódolása közben törekedtem a minél gyorsabb futásidőre, mivel az alacsony órajel miatt így is kevés processzoridő jutott az animálásra. Végül az 5 ledhez 5 char értéket rendeltem, amit egy globális char tömbön keresztül lehetett elérni. Mivel tömbről van szó, ezért nem kerülhetett a regiszterekbe az adathalmaz, azonban nem is a memória használatának kizárása, csupán minimalizálása volt a célom. Egy rövid számításon érzékeltetem a RAM méretét: 64 bájt RAM áll rendelkezésre. A regiszterek összesen 32 bájtot tesznek ki. Minden függvényhívás kezdetekor a processzor elmenti a stackre a regiszterek aktuális állapotát, majd visszatölti azokat. Ezen felül eltárolja a visszatérési címet. Azaz memóriába helyezett változókat nem számítva az első függvényhívás után 31 bájt memóriánk marad. Szerencsére a valóság nem tesz ennyire keresztbe nekünk. Mivel nem használjuk az összes regisztert, ezért a fordító kioptimalizálja a legtöbb regiszter stackre helyezését, azonban ne feledkezzünk el arról, hogy a memóriában el kell tárolni az összes lokális, és nem regiszterben tárolt globális változót is.
A SoftPWM-et megvalósító kódrészlet a Timer periféria Overflow eseményére generálódott interrupt szubrutinjában kapott helyet. Figyelni kell továbbá arra, hogy a kimeneti jelforma ne legyen dzsitteres. Ehhez egy ideiglenes tárolóban kell eltárolni a következő megjelenítendő “képkockát”. A probléma abból adódik, ha egy függvényen belül kétszer egymás után állítjuk a kimenetet ellentétesre, ekkor a LED nagyon enyhén világíthat.
A fenn említett interrupt teker egy tick változót is, ennek szerepét a következő bekezdésben tárgyalom.
A program további jellegzetessége, hogy könnyedén írható hozzá új animáció. Ehhez semmi mást nem kell tennünk, mint egy előre definiált struktúrát kitölteni az animációnk adataival, majd hozzáadni a struktúránkat a tömbhöz, amiből a program kiválasztja az animációkat. A struktúrában eltároljuk annak a függvénynek a mutatóját, ami inicializálja az animáció által használt változókat, illetve annak a függvénynek a mutatóját, ami bizonyos időközönként meghívódik és frissíti (“lépteti”) a képkockát. A frissítés mértékét szintén a struktúrában kell eltárolni. Az értéket a folyton inkrementálódó tick változóval komparálom, ez alapján hívom meg a függvényt, amire a léptető pointer mutat.
Az 1 KB memóriába 4 animáció fért bele + a leállás. További optimalizálással még több animáció is elfért volna. Ehhez például a fenti struktúra tömböt, és az animációk konstansait az eepromban kellett volna tárolni.
Az üzemidő tapasztalás alapján körülbelül 18 óra, még ekkor is működött a kapcsolás, azonban már észrevehetően halványabb volt a maximális fényerő.
Szerencsére általában van lehetőség elegendően erős mikrokontroller használatára, és a teljes mértékben szükségtelen ilyen mértékű optimalizálást végezni. Ez főleg ott látszódik, hogy hasonló árkategóriában is létezik sokkal erősebb MCU.