Az előző készülékem egy Lumia 520-as telefon volt. Az idő teltével felmerült bennem az igény a zseblámpafunkció iránt. Barkácsolási szokásaimhoz híven nem új készüléket választottam.
A készüléket az akkori kedvező ára, illetve ár-érték aránya miatt választottam. A későbbiekben számos probléma merült fel. Pár hónappal azután, hogy megvettem a készüléket elindult a Messenger divathódítása. Először a gyári üzenetküldő alkalmazásba került támogatás, majd az az API megváltoztatásával megszűnt. Végül kijött egy erősen kifogásolható minőségű alkalmazás. A későbbiekben a végtelen multimédiát tartalmazó weboldalak betöltése is problémát okozott az összesen fél gigabájtnyi memória miatt. A memóriából folyamatosan helyet foglal a videomemória is. Később minden probléma megoldódni látszott a Windows 10 megjelenésével, azonban az hivatalosan sose érkezett meg a telefonra. Nem hivatalos leírások alapján telepítve pedig botrányos teljesítményt tapasztalhattunk. Mindennek a tetejébe a MS megszüntette a jó minőségű térkép- és navigációszolgáltatást is. A legújabb Messenger szoftver egyébként már megköveteli a Windows 10-es op. rendszert. Elég volt ennyi a történelemórából, térjünk rá az érdekes részre.
Korábban:
Előző karácsonykor jelent meg komolyan az igényem egy vaku iránt, miután az egyik körtag (Bazsi) felvetette milyen jó is lenne. Ekkor még talán ő se sejtette, hogy megvalósítom az ötletét. A cél az volt, hogy a kétállású kameragombot fókuszig és a hangerő fel gombot egyszerre megnyomva bekapcsoljon a vaku, majd ismételt gombnyomásra ki. Végül egy ATTiny13A-val oldottam meg a helyzetet. Eredetileg az egyidejű lenyomást dióda-logika logikával érzékeltem volna, azonban nem találtam a műhelyben Schottky diódát, a hagyományos diódának pedig túl nagy volt a nyitófeszültsége 1.8V-os logikához. A végső megoldás az lett, hogy a hangerő fel gombra egy interrupt mindig feléleszti a mikrovezérlőt, és az vagy bekapcsolja a PWM perifériáját vagy ki. A PWM láb egy NPN tranzisztoron keresztül kapcsolgat egy LED-et, aminek az anódja viszont már nem a processzor 1.8V-os tápkörére van kötve, hanem a Li-ion akkumulátor + termináljára. Utólagos munkaként belekerült az a plusz feature, miszerint alapból halvány fényerőre kapcsolja be a LED-et, majd hangerő fel gombot nyomogatva fokozatosan erősödik a fényerő. A kikapcsolás szintén a gombkombinációval történik.
Az elhelyezéshez egy Proxon kézi szerszámmal kivágtam a megfelelő méretű darabot a telefon dobozából, majd elhelyeztem a telefon alaplapja fölé a legvékonyabb kéznél lévő FR4-re ültetett áramkört. A gombokhoz való huzalozást szintén minél vékonyabb kábellel valósítottam meg a szűk hely miatt.
Az utolsó verzió kapcsolási rajza:
Illetve az alkalmazott firmware forráskódja:
#include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #define F_CPU 128000UL #include <util/delay.h> //!!!Programozaskor az orajelet minel lentebb kell allitani, kulonben nem mukodhet megfeleloen az ATtiny13A az alacson feszultseg (1,8V) miatt #define alap_kitoltes 10 #define max_kitoltes 110 #define kitoltesi_leptek 10 volatile unsigned char allapot asm("r3"); //hamis=led kikapcsolva; igaz=led bekapcsolva ISR (INT0_vect){ cli(); _delay_ms(500); if (!(PINB & (1<<PB3)) && !(PINB & (1<<PB1))) allapot = !allapot; _delay_ms(100); sei(); } int main(void){ DDRB = (1<<PB0); //beallitjuk az egyetlen kimeneteti port iranyat kimenetre PORTB = (1<<PB2) | (1<<PB4) ; //Az uresen hagyott labakat felhuzzuk //A ket bemenetet nem huzzuk fel, mert az SoC-nel van felhuzas //A LED vezerleset nullan hagyjuk, alapallapotban nem vilagit TCCR0A = (1<<COM0A1) | (1<<WGM01) | (1<<WGM00); //OC0A kimenet hasznalata PWM-hez, Fast PWM mode. //Tulcsordulasnal 1-re allit, amikor eleri OCR0A-t, //akkor torol ==> OCR0A novelesevel no a kitoltes ==>no a fenyero set_sleep_mode(SLEEP_MODE_PWR_DOWN); //A sleep utasitaskor az ATtiny powerdown modba lep GIMSK = (1<<INT0); //Bekapcsoljuk a kulso interrputot allapot = 0; OCR0A = alap_kitoltes; //bellaitjuk a kitoltesi tenyezot (fenyerot) alapertekre, //igaz az allapot 0-n van es a while ciklus elso feltetele inicializalna, //de kaphatunk interruptot abban az 1-2 orajelciklusban is, bar ez ritka eset lenne, azonban vegzetes sei(); //interruptok engedelyezese while (1){ //az allapotgep megvalositasa. if (allapot){ TCCR0B = 1<<CS00; //tranzisztor hatarfrekvenciaja alatt maradunk igy is, clock source engedelyezese if (!(PINB & (1<<PB3)) && (OCR0A < max_kitoltes)){ //ha megnyomjuk a hengero fel gombot es meg nem ertuk el a maximalis kitoltest, akkor OCR0A += kitoltesi_leptek; //megnoveljuk a kitoltest _delay_ms(300); while (!(PINB & (1<<PB3))); //amig nyomva tartjuk a gombot nem csinalunk semmit (elkerulve, hogy folyamatosan novelje a kitoltest a maximumig) _delay_ms(300); } } else{ OCR0A = 0; //bellaitjuk a fenyerot alapertekre _delay_ms(10); TCCR0B = 0; //leallitjuk a szamlalot sleep_enable(); //Sleep Enable bit beallitasa, hogy power down modba lephessunk sleep_mode(); //Power-Down modba helyezzuk a mikrokontrollert, az interrupt fogja innen felkelteni sleep_disable(); //Sleep Enable bit torlese, ahogy a katalogus javasolja } } }
Napjainkban:
Egy lelkes ember (bizonyos feherneoh) rászánta az idejét, és 4-5 darab készüléket, hogy Linux kompatibilis UEFI loadert fordítson az említett típusra. Ez sikerült is neki. Egy Sony készülékben ugyan ez a csipkészlet található meg, nem volt nehéz dolog android drivereket beszerezni, és a google mobil operációs rendszerét installálni a L520-ra. Mivel linuxon viszonylag könnyen el lehet érni az alacsony szintű perifériákat, mint a GPIO-k, újra elővettem a projektet.
Hosszú hónapok alatt leegyszerűsödött a telepítés menete. Mindössze egy Nokia Bakery nevű programot kell elindítani, és a menüből kiválasztani, mit is szeretnénk. Első indítás után azt tapasztaljuk, hogy a futtatáshoz követelmény Engineering bootloader telepítése. Valószínű a teszteléskor használsz fejlesztési darabok csupán ebben az apró részletben tértek el a végterméktől. Ezen bootloader segítségével Mass Storage módban láthatjuk a telefon belső memóriáját. Régen sokan ódzkodtak az MTP-től. Az első androidos megoldások valóban instabilak voltak néha, azonban Windows Phone-on semmilyen problémát nem tapasztaltam a megoldással. Manapság már az Androidos megoldások is beton stabilak. Az engineering bootloader telepítéséhez a WP Internals szoftvert használjuk, amit egyébként mellékeltek a Nokia Bekery-vel. A szoftveren belül pontos utasításokat találunk.
Okos eszközöknél szokás többlépcsős bootloadert használni. Ilyenkor ha az egyik szint elromlik például egy rosszul sikerült szoftverfrissítés miatt, akkor eggyel alacsonyabb szintről kicsit fapadosabb eszközökkel megmenthető a telefonunk. Az Engineering mode bootloader a másodlagos bootloaderek közül (SBL) közül a harmadik. Efölötti szintre telepíthetjük a Bakery használatával az android bootloaderjét. A Bakery ezzel együtt automata telepít nekünk egy TWRP recoveryt is, mellyel már kedvünkre tudunk ROM-okat flashelni. Én egy CyanogenMod 14.1-et telepítettem.
Innen jön az érdekesebb rész. Hogyan tovább? Nézzük meg a telefon kapcsolási rajzát, melyet egy egyszerű google kereséssel megtalálhatunk. Végigbogarászás után azt tapasztaljuk, hogy nagyon szerencsések vagyunk, ugyanis egy GPIO ki van vezetve egy TestPointra, amit feltehetőleg tudunk majd használni szoftverből.
A kezdeti örömtől lelkesedve állítsuk be az immár androidos készüléken az usb-s debugolást, valamit adjunk root jogot a telefont elérő PC-nek. PC-n nyissunk egy terminált és lépjünk be adb shell paranccsal a telefonba. su paranccsal szerezzünk root jogot. Kilistázva a /sys/class/gpio mappát örömmel láthatjuk, hogy a kernelbe belefordították a GPIO drivert, azaz nem kell új kernelt fordítanunk. A jelenlegi állás szerint a mappa létezik és tartalmaz fájlokat. Az viszont szomorúbb hír, hogy több GPIO interfészt is lát a kernel, viszont a kapcsolási rajzon nem különíthető el első ránézésre, hogy a mi GPIO-nk mihez is csatlakozik.
kisada@shaman:/tmp$ adb shell * daemon not running. starting it now on port 5037 * * daemon started successfully * nicki:/ $ su nicki:/ # cd /sys/class/gpio nicki:/sys/class/gpio # ls export gpio11 gpiochip0 gpiochip152 gpiochip190 unexport nicki:/sys/class/gpio # ls -1 export gpio11 gpiochip0 gpiochip152 gpiochip190 unexport nicki:/sys/class/gpio #
Jó lenne kideríteni, hogy mik is ezek valójában.
Felderíteni, hogy mi merre hány méter bele akartam ásni magam az eszköz Device Tree-jébe, azonban azt nem találtam se a procsfs-ben se a sysfs-ben. Elszomorodva belemásztam az Android4Lumia kernel forráskódjába. Először is fordítottam egy működő kernelt magamnak, majd telepítettem. Így már biztos voltam abban, hogy az olvasott kód pont ugyan az, ami a telefonon jól fut. Szomorúan tapasztaltam, hogy a kernel drivereit a klasszikus módon konfigurálták, azaz C forráskódba vannak szétszórtan struktúrák, és nem egy egységes device treeben. Sebaj, megkerestem az ide illő fájlokat.
kisada@shaman:~/l520/android_kernel_nokia_msm8x27/arch/arm/mach-msm$ grep -R "struct gpio_keys_button" . | cut -d: -f1 | sort | uniq ./board-8064.c ./board-8930.c
A board-8064.c volt ábécében az első. Miután megvizsgáltam találtam utalásokat.
#define GPIO_KEY_HOME PM8921_GPIO_PM_TO_SYS(27) #define GPIO_KEY_VOLUME_UP PM8921_GPIO_PM_TO_SYS(35) #define GPIO_KEY_VOLUME_DOWN_PM8921 PM8921_GPIO_PM_TO_SYS(38) #define GPIO_KEY_VOLUME_DOWN_PM8917 PM8921_GPIO_PM_TO_SYS(30) #define GPIO_KEY_CAM_FOCUS PM8921_GPIO_PM_TO_SYS(3) #define GPIO_KEY_CAM_SNAP PM8921_GPIO_PM_TO_SYS(4) #define GPIO_KEY_ROTATION_PM8921 PM8921_GPIO_PM_TO_SYS(42) #define GPIO_KEY_ROTATION_PM8917 PM8921_GPIO_PM_TO_SYS(8) static struct gpio_keys_button cdp_keys_pm8921[] = { { .code = KEY_HOME, .gpio = GPIO_KEY_HOME, .desc = "home_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEUP, .gpio = GPIO_KEY_VOLUME_UP, .desc = "volume_up_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEDOWN, .gpio = GPIO_KEY_VOLUME_DOWN_PM8921, .desc = "volume_down_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = SW_ROTATE_LOCK, .gpio = GPIO_KEY_ROTATION_PM8921, .desc = "rotate_key", .active_low = 1, .type = EV_SW, .debounce_interval = 15, }, }; static struct gpio_keys_button cdp_keys_pm8917[] = { { .code = KEY_HOME, .gpio = GPIO_KEY_HOME, .desc = "home_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEUP, .gpio = GPIO_KEY_VOLUME_UP, .desc = "volume_up_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEDOWN, .gpio = GPIO_KEY_VOLUME_DOWN_PM8917, .desc = "volume_down_key", .active_low = 1, .type = EV_KEY, .wakeup = 1, .debounce_interval = 15, }, { .code = SW_ROTATE_LOCK, .gpio = GPIO_KEY_ROTATION_PM8917, .desc = "rotate_key", .active_low = 1, .type = EV_SW, .debounce_interval = 15, }, };
Az elenevzések alapján ezek PM8921 és PM8917 típusú IC-khez készültek. Ha összehasonlítjuk az adatlapukon megtalálható lábkiosztást az L520 kapcsolási rajzának azon szimbólumával, ahova a gombok vannak kötve, akkor eltéréseket tapasztalunk.
Elkesederés helyett vegyünk egy pillantást a board-8930.c fájlra. Ebben is találunk utalásokat a használt IC-re, és a használt GPIO-kra.
#define GPIO_VOLUME_UP_PM8038 PM8038_GPIO_PM_TO_SYS(3) #define GPIO_VOLUME_DOWN_PM8038 PM8038_GPIO_PM_TO_SYS(8) #define GPIO_CAMERA_SNAPSHOT_PM8038 PM8038_GPIO_PM_TO_SYS(10) #define GPIO_CAMERA_FOCUS_PM8038 PM8038_GPIO_PM_TO_SYS(11) #define GPIO_VOLUME_UP_PM8917 PM8917_GPIO_PM_TO_SYS(27) #define GPIO_VOLUME_DOWN_PM8917 PM8917_GPIO_PM_TO_SYS(28) #define GPIO_CAMERA_SNAPSHOT_PM8917 PM8917_GPIO_PM_TO_SYS(36) #define GPIO_CAMERA_FOCUS_PM8917 PM8917_GPIO_PM_TO_SYS(37) static struct gpio_keys_button keys_8930_pm8038[] = { { .code = KEY_VOLUMEUP, .type = EV_KEY, .desc = "volume_up", .gpio = GPIO_VOLUME_UP_PM8038, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEDOWN, .type = EV_KEY, .desc = "volume_down", .gpio = GPIO_VOLUME_DOWN_PM8038, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_CAMERA_FOCUS, .type = EV_KEY, .desc = "camera_focus", .gpio = GPIO_CAMERA_FOCUS_PM8038, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_CAMERA_SNAPSHOT, .type = EV_KEY, .desc = "camera_snapshot", .gpio = GPIO_CAMERA_SNAPSHOT_PM8038, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, }; static struct gpio_keys_button keys_8930_pm8917[] = { { .code = KEY_VOLUMEUP, .type = EV_KEY, .desc = "volume_up", .gpio = GPIO_VOLUME_UP_PM8917, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_VOLUMEDOWN, .type = EV_KEY, .desc = "volume_down", .gpio = GPIO_VOLUME_DOWN_PM8917, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_CAMERA_FOCUS, .type = EV_KEY, .desc = "camera_focus", .gpio = GPIO_CAMERA_FOCUS_PM8917, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, { .code = KEY_CAMERA_SNAPSHOT, .type = EV_KEY, .desc = "camera_snapshot", .gpio = GPIO_CAMERA_SNAPSHOT_PM8917, .wakeup = 1, .active_low = 1, .debounce_interval = 15, }, };
Ráguglizva a pm8038 kulcsszóra rögtön egy tucat Aliexpresses hirdetés fogad minket. Ezek címében szerepel, hogy Lumia520-hoz árulják helyettesítő IC-ként. Ez önmagában elég bizonyíték lenne ahhoz, hogy ezt használják. Az adatlapban egyébként ellenőrizve láthatjuk, hogy az itt feltüntetett GPIO azonosítók megegyeznek a kapcsolási rajzon szereplőkkel. Illetve a kapcsolási rajzon látható funkciók megegyeznek az adatlapon ajánlotakkal. Akinek még ez se elég, a kapcsolási rajzon a bal alsó sarkoban fel van tűntetve, hogy szerepel az IC az oldalon.
Ez egy jó előrelépés volt, láthattam hogy áll össze ez az alrendszer. A strúktúrában látható egyébként a .code elem, ami azt a kódot tralmazza, amit az oprendszer meg fog kapni, mint lenoymott billentyű. Ezáltal a felhasználói programoknak nem kell foglalkozni azzal, hogy honnan jött a gombnyomás, elég azzal, hogy milyen funkciót lát el az a gomb.
Az aliexpresses IC-k ötletet adtak a továbbhaladáshoz. Keresek IC-ket a telefonhoz, és beazonosítom őket a kapcsolási rajz alapján. Így talátam meg a WCN3660 IC-t, ami sajnos még mindig nem az, amihez a vakut tudom kapcsolni.
Több tudást nem hozott az aliexpresses módszer. A kapcsolási rajz ésszerű elemzésével folytattam. Az eMMC-t be lehet azonosítani a kapcsolási rajzról. Kizárásos alapon a bal alsó sarokban található IC lista alapján beazonosítható az MSM8227 is, mint SoC. És idáig érdemes a cél érdekében elemeznünk, ugyanis ezzel már megállapítottuk, hogy a GPIO-nk az MSM8227-re van bekötve közvetlen. Ez egyébként sejthető volt a mellette feltüntetett lábakból.
Vissza a linuxhoz. Listázzuk ki, hogy a fent talált gpio vezérlők hova is tartoznak.
nicki:/sys/class/gpio # cat *chip*/label msmgpio pm-gpio pm8xxx-mpp nicki:/sys/class/gpio #
Már tudjuk, hogy nekünk nem a PowerManagment IC-n keresztül kell kommunikálnunk a GPIO-val, hanem az MSM processzoron keresztül. Logikus, hogy az msmgpio alatt találjuk a portunkat. Láthatjuk, hogy az msm csip a gpiochip0 mappa alatt található. Ez azt jelenti, hogy a csipen belül a GPIO számozásokat 0-val kell eltolni, hogy megkapjuk a linuxos sorszámukat. Azaz a mi portunk sorszáma a 115.
Rögtön tegyünk is egy próbát az írhatóságra.
kisada@shaman:~/l520/AnyKernel-master/bootimg$ adb shell nicki:/ $ su nicki:/ # cd /sys/class/gpio/ nicki:/sys/class/gpio # echo "115" > export nicki:/sys/class/gpio # cd gpio115 nicki:/sys/class/gpio/gpio115 # ls active_low device direction edge power subsystem uevent value nicki:/sys/class/gpio/gpio115 # cat direction in nicki:/sys/class/gpio/gpio115 # echo "out" > direction nicki:/sys/class/gpio/gpio115 # cat direction out nicki:/sys/class/gpio/gpio115 # cat value 0 nicki:/sys/class/gpio/gpio115 # echo "1" > value nicki:/sys/class/gpio/gpio115 # cat value 1 nicki:/sys/class/gpio/gpio115 # echo "0" > value nicki:/sys/class/gpio/gpio115 # cat value 0 nicki:/sys/class/gpio/gpio115 #
Siker! 🙂
Akkor jöjjön az új hardver.
Egy nagyon egyszerű FET-es kapcsolást állítottam össze a célra. Ebben a verzióban helyet kap több védelmi áramkör is, azaz ESD védő diódák és egy Multifuse biztosíték. Ha lúd, akkor legyen kövér alapon a maradék helyre kivezettem a processzor UART lábait is. Beültetés közben azt a szomorú felfedezést tettem, hogy a kiszemelt GPIO testpadje hozzáférhetetlen helyen van. Ehelyett az msm_boot_config[6] lábbal próbáltam ki a kapcsolást kísérletként.
A hardver után örömmel tapasztaltam, hogy a terminálból állítgatva működik a vaku. Megnyugtató volt, hogy a boot_config láb húzása nincs hatással a bootolásra.
nicki:/ # echo "112" > /sys/class/gpio/export nicki:/ # echo "out" > /sys/class/gpio/gpio112/direction nicki:/ # echo "1" > /sys/class/gpio/gpio112/value nicki:/ # echo "0" > /sys/class/gpio/gpio112/value
Annak érdekében, hogy a GPIO-t ne kelljen exportálni mindig, illetve alapértelmezetten kimenetre legyen állítva, létrehoztam egy saját init scriptet, amit ezeket a parancsokat boot időben lefuttatja.
#!/system/bin/sh # # Set up GPIO for falshlight # echo "112" > /sys/class/gpio/export echo "out" > /sys/class/gpio/gpio112/direction echo "0" > /sys/class/gpio/gpio112/value
Amikor ezzel is megvoltam úgy döntöttem, hogy telefonos alkalmazásból is szeretném vezérelni a vakut. A kamera alkalmazás azért nem jöhet sajnos szóba, mert mint kiderült, még nincs az L520 kamera drivere integrálva a jelenlegi android verzióba. Ha a fejlesztők megoldják ezt a problémát, akkor én is integrálom majd a vakut a “gyári” alkalmazásba. Jelenleg egy nagyon egyszerű alkalmazással lehet kapcsolni a LED-et. A forráskódot egyszerűsége miatt nem mellékelem a cikkbe, viszont kiemelném a githubon elérhető osztályt, ami segített a munkában: link
Felmerül a kérdés, hogy miért GPIO-t használtam. Alternatív lehetőségként ajánlkozik, hogy használjuk az I2C buszt, azon keresztül történjen a kommunikáció az op. rendszer és a LED között. Ezzel az elméleti bökkenő az, hogy komplikált utólagos bővítésnél biztosítani a jelintegritást. A gyakorlatibb probléma az, hogy az előző konstrukcióban is kicsit eltartotta a SO-8 tokozású mikrovezérlő a hátlapot. Ezek mellett megállapítható, hogy nyugalmi állapotban a tisztán FET-es megoldás kevesebbet fogyaszt, mint egy plusz mikrovezérlő. Egy hordozható eszköznél fontos az akkumulátoridő.