> Velká část low level kódu pro Renesas je napsán v C. Proč v roce 2024 se stále low level kód píše v C za použití maker a globálních proměnných?
Protože Renesas dodává MCU i velmi konzervativním zákazníkům a potřebuje aby jeho něco jako SDK byli schopni zkompilovat jak zákaznící používajcí komerční certifikované (orazítkované) kompilátory pro automotive, tak zákaznící vyvíjející na poměrně modenrím C++ stacku. Píší to podle C99 a MISRA C a možná ještě nějaké další standardy dodržují.
> Chcete mi tvrdit, že máme statický framebuffer?
A co je na něm špatně? Align je tam podle mě zbytečně. Možná kvůli potřebě deterministického přístupu v obsluze přerušení, ale to je stejně na Cortex-M4 irelevantní. Celá ta knihovna alokuje vše s čím pracuje staticky! A to včetně těch na kterých knihovná závisí nebo je využívá. Podotýkám, že včetně těch, které renderují animovaný text! Nebudu polemizovat jestli je to nutné nebo ne, protože reálná odpověď je že to záleží na kódu okolo, ale debugovat ty mezní případy je všeobecně peklo, takže já osobně tento přístup vítám i za cenu že alokace některých bufferů a práce s nimi je dost hackování přes makra.
> Dobrý programátor makra nepoužívá.
Ale no tak. Třeba vývojáře Linuxového kernelu považuji za "dobré programátory", a makry je kód kernelu prolezlý od shora dolů. A když se vrátíme trochu víc zpět k embedded, tak v Zephyru se taky makra používají v extrémním množství a realita je taková, že většina lidí ty over-enginnered subsystémy implementované čistě pomocí maker oceňuje. Já mám odlišný názor, ale zdaleka ne tak extrémní jako "Dobrý programátor makra nepoužívá.".
> knihovně je napsáno 10000. To znamená, že knihovna volá 10000× obsluhu přerušení. Docela velké číslo na řízení 96 diod?
Začneme formalitou výrok "To znamená, že knihovna volá 10000× obsluhu přerušení." je sám o sobě chybný, protože když to necháte běžet déle než sekundu, tak se to zavolá více než 10000×. Polde mě jste myslele 10000× za sekundu. To je 10 kHz a to na maticový display není žádné velké drama. V závislosti na HW návrhu může u maticových displayů existovat i minimální nutná frekvence a duty cycle, která by vycházela, že výrobci LED povolují pulzně "posílat" diodami větší produy (typicky 10x větší produy!). Výrobci toho využívají právě v takových spínaných displayích , kde pak jde dosahovat většího jasu. Nicméně takové použití má podmínky a omezení. Nelze diodami "posílat" 10x vyšší produy trvale. Nicméně k prodovému přetěžování se ještě dostanu dále.
> obnovovací frekvenci 104 Hz. Tohle snad ale nedělá, že ne?
Taky není žádné velké drama. Vliv na kvalitu vnímaní to má podobný jako 120 Hz monitory. Někteří lidé to vnímají. Tím tuplem u low-res display to může dávat smysl.
> Bohužel to dělá, program opravdu funguje tak, že v každém přerušovacím cyklu je nejprve vše zhasnuto, (řádek 119) a následně se rozsvítí jedna z 96 diod (řádky 123–129).
Alespoň je to jednoduché a deterministické, že. Kdo dnes chce jednoduché a deterministické algoritmy, že?
Ale ještě se v tom trochu pošťoucháme. Jednoduchý a deterministický není jen kód a doba běhu, ale hlavně prodová zátěž. Sám dále zmiňujete, že když jich rozsvítíte 10, tak intenzita klesne :D To je jasný Stress above Absolute Maximum Ratings (při vývoji spolehlivého HW klasifikováno jako katastrofa). To je jeden z několika důvodů proč se jen málokdy dělá optimializace, že se rozvicuje v více diod najednou, pokud to není nezbytně nutné a pokud na to HW není navržen. Pojďme to ale ještě propočítat abychom zjistili jak moc špatné to je. Forward voltage červené diody je cca 1.7V. Arduino k nim dalo odpor 330. Napětí je 5V. Kirchhoffov zákon říká že proud který teče diodou je stejný jako proud který teče rezistorem. Napětí na rezistru je 5V - 1.7V = 3.3V. Proud který jimi teče je podle ohmova zákona U / R = 3.3 / 330 = 0.01 = 10mA. To se zdá být +- ok proud pro Vf 1.7V. Dle datasheetu je na portech P0xx maximální souhrnný odběr 30mA a na portech P2xx 60mA. Tyto čísla ale ve skutečnosti nejsou problém. Větší problém dělá, že sink i source jednotlivých pinů je 8 mA MAX, takže když se třeba rozhodnote pro rozvícení 2 diod ze seznamu 2,4,8,14,22,32,44,58,74,92, který v článku zmiňujete, tak ten limit vždy přepálíte. To jestli existuje nebo neexistuje kombinace 2 rozsvícených diod, který by úplně nězávisle zatížila 4 piny (tzn. 2 source a 2 sink) asi nedokážu rychle odvodid/dokázat, ale intuitivně si myslím, že spíš neexistuje.
> To co tady nesedí je, že vlevo je MSB a vpravo je LSB.
Proč by to nesedělo? Je to navrženo tak aby to bylo kompaktní a aby se to intuitivně dalo napsat v kódu. Asi narážíte na to, že kdyby to bylo v kódu zracdově otočené, tak by se (pouze na první mřádku displaye) dalo k prvnímu pixelu přistupovat pomocí masky (1 << 0), k druhému (1 << 1), k třetímu (1 << 2), atd. Ale to by fungovalo jen pro první řádek. Pro druhý řádek by to bylo mírně složitějíš (1 << 12), a zábavné by to začalo u třetího kde by levých 8 bitů (z 12) bylo v čísle v pravo (1 << 24) a zbyvající pravé bity byli v následujcím (bajtu) u32 úplně vlevo. To že fyzicka pravé část je v čísle úplně vlevo je trochu problém, protože ten příástup na hranicích byste podle mě musel ifovat, ne? Takhle ty bity jdou sekvenčně stejně jako na fyzickém displayi, liší se jen interval zalomení "řádku" (na display je zalomení po 12 pixelech, v čísle po 32 bitech).
Tady ten formát má také výhodu, že tím že sekvnčnost bitů odpovídá sekvenčnosti na display, tak s trocu snahy jde udělat sed, který ten frame umí nakreslit:
echo "0x3184a444, 0x42081100, 0xa0040000" | sed -e "s/0x//g" -e "s/, //g" \
-e "s/0/ /g" \
-e "s/1/ X/g" \
-e "s/2/ X /g" \
-e "s/3/ XX/g" \
-e "s/4/ X /g" \
-e "s/5/ X X/g" \
-e "s/6/ XX /g" \
-e "s/7/ XXX/g" \
-e "s/8/X /g" \
-e "s/9/X X/g" \
-e "s/a/X X /g" \
-e "s/b/X XX/g" \
-e "s/c/XX /g" \
-e "s/d/XX X/g" \
-e "s/e/XXX /g" \
-e "s/f/XXXX/g" -E -e "s/(.{12})/\1\n/g"
> Tato funkce se volá při kopírování dat do framebufferu, což se děje při každé změně animačního frame.
Ono se to děje při načítaní jakéhokoliv frame, i neanimačního. Viz. voláni next() např. v loadFrame() ale i v IRQ. A přitom kdyby to pochybně (UB, funguje správně jen na little endian) přes memcpy nekonvertovali u32 -> u8. Možná ale předpokládali nějaký budoucí port pro 8-bit MCU a chtěli se vyhnout pak náročnějším bitovým operacím v IRQ (to je ale fuk, když tam pak občas stejně volají next).
> Například se dozvíme, že řízení se používají piny P003, P004, P011, P012, P013, P015, P204, P205, P206, P212, P213. Na obrázku se ale žádná taková označení nepoužívají, ale zas tam najdu čísla 7,3,4… a dále pak ROW0, ROW1, ROW2. Jaká je mezi tím souvislost.
Souhlasím, že ta schémata kreslená ve Altium jsou nepraktická. Ty tmavé nápisy nad vodiči jsou lokální identifikátory. Opakované použití ve stejném listu znamená propojení. Použití napříč listy jsou nepropojená. Ty žluté markery 1, 2, 3, ... jsou globální symboly a ty jsou exportovány do "vnějších" listů hierarchie. Na první straně je pak onene LEDMATRIX.SchDoc referencován a tyto globální symboly jsou mapovány na lokální indetifikátory např. P003, který je pak referencován na dané straně 2x. Jednou u tho bloku, podruhe u pinu P003 na MCU. Pro prohlížení schémat doporučuji SumatraPDF, ale ani v tom ty exporty Altium schémat nefungují moc dobře.
> Dobře, co je ale P012. To je 12. pin na portu P0.
Ve skutečnosti je 11., protože P009 byl vynechán. Doporučil bych se ale od indexování pinů odporstit. Hlavně tam jak počítáte indexy pinů v nějakém poli struktur nebo co to je. To jsou totiž generované kódy a nemáte žádnou garaci, že až Renesas vydá novou verzi jejich generátoru kódu (který nabízejí v rámci "ekosystému", kterému říkaji FSP - Flexible Software Packages), tak ty indexy mohou být úplně jinak. To že jsou piny display v nějakém poli za sebou taky nemusí být dlouhodobě pravda. Ono to tak teď je asi protože když to v tom grafickém klikátku naklikávali, tak je naklikali po sobě. Ale až se je někdy Renesas rozhodne sesortovat, zak znovuobjevíte, že ten display je připojeny na dva porty: P0xx a P2xx a že mezi posledním pinem z portu 0 a prvním z portu 2 jsou další piny....
> Pokud nechytáte kontext tak v Arduinu jsou R_PORT0 a R_PORT2 globální proměnné. Navíc jsou to makra, která obsahují nějaký šílený pointerový přepočet
No takže sám sobě odpovídáte, že to nejsou proměnné (nezabírají místo v paměti). Ten přepočet není šilény. Měl by to být cast absolutní adresy perefirie na pointer na strukturu, přes kteoru se pak přistupuje ke konkrétnímu registru dané periferie. Celé se to typciky vyhodnotí v compile time a v binárce typicky bude přímo adresa registru (ani ta struktura, ani ten pointer, ani ten offset). Ten hlavičkový soubor je typicky generovaný ze SVD a typicky toto funguje u všech vendorů s ARM MCU úplně stejně.
> snažil jsem se najít nastavení vysoké impedance a neuspěl jsem.
IOPORT_CFG_PORT_DIRECTION_INPUT
> Malou nevýhodou je kolísavý jas, který může být lehce znatelné v animacích, které aktivují v každém frame různý počet diod. Toto se může projevovat jako šum v pozadí.
To jestli je to malá nebo velká nevýhodaje relativní. Mě to třeba přiapdá jako dost zásadní nevýhoda, proto se osobně přikláním na stranu rozsvěcovat diody po jedné, včetně "plýtvání času" se "zhasínáním" diod, které ani nesvítily. Bze ohledu na to kolik jich svítí nebo ne to pak má konstantní jas a navíc neporušuje Absolute Maximum Ratigns čipu (a diod přáípadně i diod, ale to u Arduina není problém).
Intenzivně se zabývám programováním zejména v jazyce C++. Vyvíjím vlastní knihovny, vzory, techniky, používám šablony, to vše proto, aby se mi usnadnil život při návrhu aplikací. Pracoval jsem jako programátor ve společnosti Seznam.cz. Nyní jsem se usadil v jednom startupu, kde vyvíjím serverové komponenty a informační systémy v C++
Přečteno 51 057×
Přečteno 23 933×
Přečteno 22 867×
Přečteno 20 949×
Přečteno 17 755×