ESP32 - spustenie programu z RAM
Po dlhšej dobe som sa vrátil k svojmu koníčku - práci s hardvérom. Pri tejto príležitosti tu budem písať krátke návody, poznámky, problémy, na ktoré som narazil, riešenia atď. Trochu neplánovane som začal už v svojom predchádzajúcom blogu, kde som riešil hello world na jednom obskurnom embedded systéme.
Predstavenie hardvéru
Ako vývojovú dosku som si kúpil TTGO-T8 V1.7. Pôvodne som chcel použiť ESP32-WROVER-IB, ale nenašiel som obchod, ktorý by mi poslal adaptér (breakout board) s lacnejším poštovným než celá vývojová doska z číny. Takže okrem samotného modulu tu mám zbytočnosti ako nabíjací obvod pre li-ion baterku, USB port, UART prevodník atď. Jednoducho hardvér, ktorý buď nepotrebujem, lebo už mám v šuflíku (UART), alebo nebudem potrebovať vôbec (li-ion).
Samotný hardvér je podľa mňa hrozný šmejd. Na prvý pohľad vyzerá dobre, ale oscilátor si kľudne tiká namiesto 40MHz na 41MHz.
esptool.py v2.9-dev Found 1 serial ports Serial port /dev/ttyUSB0 Connecting.... Detecting chip type... ESP32 Chip is ESP32D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz
Vstavaný USART prevodník je SIL 2104, ktorý zvláda prenos rýchlosťou 2Mbps, čo je priemernej veľkosti hello world-u vítaná vlastnosť.
Architektúra
ESP32 obsahuje 2 mikroprocesory Tensilica Xtensa LX6 taktovaný na 80-240 MHz. Procesor má harvardskú architektúru - kód a dáta sú adresované pomocou samostatných zberníc.
K procesoru býva cez SPI pripojená externá flash pamäť a voliteľne externá RAM. Prístup k pamätiam riadi MMU jednotka, ktorá podľa mojich informácii nie je konfigurovateľná. MMU sprístupňuje flash pamäť cez inštrukčnú aj dátovú zbernicu, takže je možné z nej spúšťať kód a zároveň aj pristupovať k dátam. Externá RAM je sprístupnená len cez dátovú zbernicu (nie je teda možné využiť túto pamäť na spúšťanie kódu).
Pre zrýchlenie prístupu k externým pamätiam sa používa cache. Mimochodom dosť deravá cache, takže nie vždy sú dáta tesne po zápise správne prečítané. Nás však externé pamäte nebudú zaujímať pretože kód nemôžme spustiť z externej RAM.
Mikrokontrolér disponuje 512kB internej RAM pamäte. Tá je pomocou MMU sprístupnená zase na rôznych zberniciach. Časť je dostupná cez dátovú zbernicu, časť cez inštrukčnú a časť je rezervovaná. Pre nahranie vlastného programu môžme využiť len 128kB pamäte na adrese 0x4008 0000. Na celý program je to síce málo, ale na napísanie a odladenie niektorej časti, napríklad ovládanie LCD je prakticky ideálna.
Prečo?
Prečo nie? ;)
Ale teraz vážne. Zápis programu do pamäte trvá určitú dobu, flash pamäť sa častým prepisovaním opotrebuje a aj keď nemusí dôjsť k jej zničeniu počas programovania môže to viesť k zníženej spoľahlivosti neskôr pri prevádzke zariadenia.
Ako na to?
V dokumentácii k možnosti nahrať program do RAM prakticky nič nie je, fóra sú tak použiteľné ako každé fórum, kde sú nadšenci do arduina, takže nezostáva nič, len skúsiť a pozrieť, čo sa bude diať ;)
Začneme skopírovaním príkladu blink z esp-idf: cp -r /cesta/k/esp-idf/examples/get-started/blink blink
. V adresári blink spustíme idf.py menuconfig
. V sekcii Build type / Application build type vyberieme ELF file, loadable into RAM. Kód skompilujeme štandardne príkazom idf.py build
, alebo ninja -C build
.
Skompilovaný program, by mal obsahovať len segmenty umiestnené v RAM. Skontrolovať to môžme pomocou príkazu xtensa-esp32-elf-objdump -h build/blink.elf
, alebo disassemblerom xtensa-esp32-elf-objdump -d build/blink.elf
.
Disassembly of section .iram0.vectors: 40080000 <_WindowOverflow4>: 40080000: 49c500 s32e a0, a5, -16 40080003: 49d510 s32e a1, a5, -12 40080006: 49e520 s32e a2, a5, -8 40080009: 49f530 s32e a3, a5, -4 4008000c: 003400 rfwo ... 40080040 <_WindowUnderflow4>: 40080040: 09c500 l32e a0, a5, -16 40080043: 09d510 l32e a1, a5, -12 40080046: 09e520 l32e a2, a5, -8 40080049: 09f530 l32e a3, a5, -4 4008004c: 003500 rfwu ...
ELF binárka sa musí pred nahraním konvertovať nástrojom elf2image.
esptool.py --chip esp32 elf2image build/blink.elf
Binárka sa nahraje a spustí esptool-om pomocou príkazu load_ram
.
esptool.py --chip esp32 --baud 921600 --no-stub load_ram build/blink.bin
Na pripojenie k UART sa nesmie použiť idf.py monitor
, pretože ten pri pripojení resetuje mikrokontrolér, kvôli čomu by sme prišli o obsah RAM. Namiesto toho sa pripojíme pomocou screenu (screen /dev/ttyUSB0 115200
), alebo picocomu (picocom /dev/ttyUSB0 -b115200
), alebo iného obľúbeného programu. Pripomeniem, že screen sa ukončuje pomocou skratky Ctrl+A
a potom :quit
. Picocom sa ukončuje skratkou Ctrl+A, Q
(najskôr Ctrl, potom stlačiť A, pustiť A a stlačiť Q).
Jednoduché, nie?
Prečo na tak jednoduchý postup doteraz nikto neprišiel?
Nuž možnosť, ktorú som zvolil v menu (ELF file, loadable into RAM) je novinka, ktorá v predchádzajúcich verziách nebola dostupná. Táto možnosť zmení oblasti pamäte v linker skripte. Za bežných okolností je linker skript pomerne jednoduchý. Zvyčajne. ESP32 má dosť šialené mapovanie pamäte, takže nie je to úplne triviálne. Keď k tomu prirátame komunitu typických arduinovských lepičov, tak bolo jasné, že sa do toho nikto z komunity nepustí.
Práve keď som sa do toho chcel pustiť som aktualizoval esp-idf a jeje ono to niekto urobil predomnou ;)
Pre pridávanie komentárov sa musíte prihlásiť.
Rozlisuj vypnut a definitivne vypnut screen a pod. programy.
Musel som si to prečítať na viac krát, páč furt ma niekto vyrušoval, ale teším sa s tebou.
Dobrý hack, to ti dosť dosiek ušetrí pri vývoji :)