click here for English version
- = Programování = -
No tak to vezmeme od začátku: od QBASICu (se krerým jsem se setkal někdy koncem 1995) jsem se přes Pascal dostal až k C. K tomu jsem ještě pokusoval s různými assemblery (x86, Z80, uPC 51, HC11, TMS320Cxx...). Pascal byl po dlouhou dobu mým nejoblíbenějším jazykem. Ovšem jeho překladač Borland Pascal 7.0.1 je dnes již pěkný stařeček, který na nové CPU už nestačí. Existuje sice 32-bitový překladač GNU FreePascal, který zvládá pmode, ale... Říkal jsem si, že když už začít s něčím novým, tak s C. Zájemce o FreePascal odkážu na web INT21h, kde se mu pár autorů věnuje.
C je v současnosti asi nejrozšířenější jazyk vůbec a tak na různých platformách (od superpočítačů po mikrokontroléry) máte hned několik překladačů. Tím nejvhodnějším pro DOS je DJGPP (GCC port). O tom, že to není žádný salát by vás měl přesvědčit např. Quake od id Software nebo nový engine pro Hexen II: Hammer of Thyrion.
Přechod z Pascalu na C není nijak jednoduchý a já moc děkuji Redoxovi/MovSD, že mi poskytl mnohé cenné rady ohledně DJGPP a podpořil mě tak na té strastiplné cestě ;-). Taky můžu vřele doporučit knížku Pavel Herout: Učebnice jazyka C z nakladatelství Kopp, kde jsou často uváděny analogie v Pascalu. Ovšem na Pascal jsem samozřejměnezapomněl. Pokud je potřeba udělat rychle nějakou malou low-level utilitu, je Pascal asi tím nejlepším. UPDATE: Pascal už jsem zapomněl, jak jsem zjistil a nijak mi to nechybí ;) BTW zde je zajímavý článek o pohledu na genetiku a DNA očima kodéra.
- = DJGPP = -
+ DJGPP je GPL OpenSource software, to znamená že si ho stáhnete zadarmo i se zdrojáky. + GCC je stále vyvíjený překladač a jeho aktuální verze je 12.2.0 (vyšla před nedávnem). + Jsou k dispozici cross-compilery pro Win32/64, Linux32/64 (Debian), Mac OS X a GCC-IA16 s knihovnou libi86 pro target DOS 8086 + Rozsáhlé možnosti optimalizace kódu a podpora instrukčních sad moderních CPU. + Obsahuje vlastní kvalitní DPMI server, velký jen 20 kB proti 260 kB extenderu DOS4/GW, který používá Watcom C. + Protože pracujete v chráněném režimu (pmode) je všechna paměť PC vaše a ještě k tomu až 2048 MB swapfile. + Obsahuje řadu Posix/Linuxových funkcí, což pomůže při portaci software. + Částečná podpora dynamicky nahrávaných modulů: DXE3, DLM a DLX + Propracované Borland-like vývojové prostředí RHIDE, nyní je k dispozici i FlDev v grafickém režimu. + Mnoho dalších plus, o kterých ještě třeba nevím ;-) - Trochu krkolomný přístup k hardwaru a BIOSu a prostě všemu realmodovému, ale to se dá zvládnout. V DJGPP jde udělat i rezident! - Větší výsledné binárky oproti třeba Borland C, "Hello World" se dá stripnout a spakovat asi na 40 kB.
Aktuální verzi DJGPP 2.05 si můžete stáhnout z oficiálního FTP: ftp://ftp.delorie.com/pub/djgpp/current a zde najdete případně novější buildy GCC od Andrise Pavenise. V aktuální verzi balíčku DJDEV 2.05 z 18.10.2015 je chyba ve funkci valloc(), která už byla opravena v CVS repozitáři, ale dosud nikdo nezkompiloval oficiální release. Proto jsem si systémové knihovny přeložil sám a můžete si je stáhnout zde (aktuální zdrojáky k 11.1.2018). Při kompilaci knihovny OpenJPEG 1.5.0 jsem narazil na chybu ve funkci void *memalign(size_t _align, size_t _amt), která pro parametr _align > 8 nefunguje správně a programy využívající OpenJPEG crashovaly. Problém jsem v tomto konkrétním případě obešel úpravou souboru opj_malloc.h, kde jsem použil funkci _mm_malloc(size_t size, size_t align) z mm_malloc.h, která dělá to samé, jen má opačné pořadí argumentů a funguje správně i pro align velikosti 16. Zkompilovaná knihovna OpenJPEG 1.5.0 je zde.
Po rozbalení balíčků je potřeba nastavit systémovou proměnnou DJGPP, aby ukazovala správnou cestu k souboru DJGPP.ENV - např. SET DJGPP=E:/DJGPP/DJGPP.ENV (ano, zde mají být normální lomítka) a nastavit systémovou cestu do adresáře DJGPP/BIN - např. PATH=E:\DJGPP\BIN;%PATH%. Aby se mi v systému nemlátily různé verze GCC, používám pro nastavení dávkový soubor SETDJ.BAT v cestě, který si zavolám z libovolného pracovního adresáře. Pokud chcete změnit výchozí úložiště dočasných souborů, které je v adresáři DJGPP/TMP, změňte na začátku souboru DJGPP.ENV řádek +TMPDIR=%DJDIR%/tmp např. na +TMPDIR=%TEMP% pro použití systémové proměnné TEMP.
Dále se vám může hodit komplexní grafická a zvuková knihovna Allegro http://www.talula.demon.co.uk/allegro (je potřeba stáhnout starší verzi, v současné už podpora DJGPP bohužel chybí). Pro portování grafických linuxových programů přijdou vhod knihovny Nano-X, Microwindows, NXlib a FLTK pomocí nichž byl nedávno portován pro DOS webový prohlížeč Dillo, více zde. Diskusní fórum o DJGPP na Google Groups: http://groups.google.com/group/comp.os.msdos.djgpp.
Pokud na vašem PC neběží nějaký DPMI server, jako třeba QDPMI, HDPMI nebo snad Windows, tak tady je nezbytné CWSDPMI7.ZIP [71 kB] nyní ve verzi 7, společné pro všechny programy z DJGPP. Novinkou od verze 5 je CWSDSTUB.EXE, který umožní běh programu bez externího DPMI serveru. Stačí program zkonvertovat do formátu COFF (exe2coff mypgm.exe), tím se odřízne standardní stub, a připojit k němu na začátek cwsdstub.exe (COPY+APPEND v DOS Navigatoru nebo COPY /B cwsdstub.exe + mypgm mypgm.exe). Na Windows Vista a vyšších bylo zavedeno omezení, že ve výchozím stavu systém poskytuje jen 32 MB DPMI paměti DOSovým programům. Toto nastavení je možné změnit v registrech úpravou proměnné DpmiLimit. Na Windows 10 je zas problém, pokud je tato hodnota příliš velká.
U GCC od verze 3 se objevil problém s casesensitivitou příkazového řádku. Pokud GCC předáváte názvy souborů velkými písmeny, může se stát, že začne vypisovat nesmyslné chyby ve zdrojovém kódu (jak jsem zjistil později, soubory s velkými písmeny se zpracovávají jako C++ nehledě na to, že mají příponu pouze .C). Napsal jsem proto malý prográmek GCC Launcher, kterým se nahradí původní GCC.EXE, jenž se přejmenuje na GC_.EXE. Launcher lowercasuje názvy souborů a takto upravené argumenty předává původnímu GCC, tedy GC_.EXE.
Pozor na UPX 3.x a GCC 4.x.x! Některé novější verze DJGPP GCC 4.x.x balíčků byly komprimovány UPX 3.01 s parametrem --brute, který používá nově LZMA kompresi, jenž sice produkuje o něco menší soubory ale dekomprese trvá oproti NRV mnohanásobně déle. Proto dpoporučuji .EXE soubory překladače rozbalit pomocí UPX -d *.EXE a znovu zabalit metodou NRV UPX --best *.EXE. V mojem případě se tak doba překladu zkrátila asi na polovinu (u libjpeg v0.6 z 41s na 21s).
Pokud máte problém s tím, že překladač při kompilaci vychrlí spoustu chybových hlášek, které nelze zastavit na konci obrazovky ani přesměrovat do souboru, zkuste tento malý rezidentní prográmek v assembleru: STDERROR.ASM. Ten jednoduše přesměruje stream STDERR, který jde v DOSu výlučně na obrazovku na stream STDOUT, který již lze klasicky filtrovat (např. |more, |grep...) nebo přesměrovat do souboru ( >error.log). Další možnost je použít inteligentnější DOSový shell, např. 4DOS nebo BASH, které umí se STDERR lépe pracovat.
Zde je pár prográmků, které jsem v DJGPP napsal a můžete je používat jako freeware. Pokud něco z toho shledáte užitečné nebo objevíte nějaké chyby, dejte vědět prosím mejlem:
DJGPP a využití paměti
V úvodu jsem zmínil, že DJGPP programy mohou využívat v chráněném módu veškerou dostupnou paměť PC. To však platí různě v závislosti na konkrétním prostředí v kterém program běží a ne vždy je možno veškerou paměť použít. Sám jsem v tom měl trochu chaos, tak to rozeberu trochu podrobněji. DJGPP programy (klienti) pro práci s pamětí (jako je alokace, mapování, atd.) využívají služeb DPMI serveru (API poskytované skrze INT 31h). Každý DJGPP program má na začátku malý kousek real-mode kódu, tzv. stub, který se stará m.j. o detekci přítomnosti DPMI serveru v paměti. Pokud ho nenajde, pokusí se zavést výchozí server cwsdpmi.exe a pokud tento soubor v cestě nenajde, tak vypíše chybovou hlášku "no DPMI - Get csdpmi*b.zip". Je také možné k DJGPP programu (COFF soubor) připojit různé alternativní stuby, které už obsahují DPMI server, např. cwsdstub.exe (CWSDPMI) nebo pmodstub.exe (PMODE/DJ), který je o něco menší a implementuje méně DPMI funkcí. Tím vytvoříme jeden spustitelný soubor bez dalších závislostí, což se v některých případech může hodit. Ale dnes už většinou v systému nějaká podpora DPMI existuje, např. Windows 9x a Windows řady NT (pokud je v konfiguračním souboru autoexec.nt zaveden DPMI server dosx.exe, což tuším defaultně je).
CWSDPMI se při spuštění DJGPP programu normálně natáhne do paměti a po jeho ukončení se zase unloadne. Je možné ho v paměti nechat jako rezident, pokud ho ručně spustíme s parametrem cwsdpmi.exe -r. Avšak potom zbytečně okupuje určitou část dolní paměti pro DOS. Výrazně méně dolní paměti v rezidentním režimu spotřebuje novější Japhethův server HDPMI32. CWSDPMI server napsal v letech 1994 - 1996 Charles W. Sandmann v Borland C (některé moduly jsou v Turbo Assembleru) jako nástupce extenderu GO32, který používalo DJGPP v1. Projekt je OpenSource, dostupný zde či GitHubu. CWSDPMI podporuje funkce dle standardu DPMI 0.9 a také většinu funkcí z DPMI 1.0, nicméně reportuje verzi 0.9. Podporuje také režim stránkování a virtuální paměť, což mělo význam dříve, když bylo RAMky nedostatek. Pomocí stránkovacího souboru tak program mohl využít až 2 GB virtuální paměti, avšak s výrazně pomalejším přístupem (kdopak si dnes vzpomene na nějakou 486 s 4 MB RAM, když se zaplnila RAMka a systém začal swapovat na disk, což provázel typický zvuk chroustajících hlaviček, výkon padnul o x řádů, níže - někdy se tak operace na pár minut protáhla na několik hodin). Virtuální paměť funguje zjednodušeně tak, že správce paměti při zaplnění fyzické RAM vybere nejméně používané stránky (běžně 4 kB), uloží je na disk a uspokojí jimi požadavek nové alokace. Pokud program přistoupí ke stránce, která není ve fyzické RAM, MMU v CPU vyvolá výjimku page fault, kterou pak správce paměti musí ošetřit tím, že uvolní/odloží nějakou jinou stránku a místo ní nahraje tu požadovanou z disku. Dnes v době 64-bitových CPU a levných GB RAM se tomu můžeme jen smát, proto např. ani v HDPMI32 nebyla virtuální paměť nikdy implementována.
Poslední verze CWSDPMI r7 z roku 2010 podporuje až 4 GB RAM a velké 4MB stránky pro rychlejší přístup. Pokud máte 4 GB RAM a z toho vám třeba 0,5 GB sežere grafická karta a další MMIO, BIOS umí tuto zbylou RAM přemapovat do oblasti nad 4 GB a CWSDPMI ji umí za jistých podmínek využít. Technicky by bylo možné využít až 64 GB RAM přes PAE. DPMI server typicky běží s oprávněním v ring 0 a klient v ring 3, takže klient nemůže běžným způsobem provádět privilegované instrukce. To lze obejít buď jednoduše použitím alternativního DPMI serveru, který klienty pouští v ring 0 - cwsdpr0.exe nebo složitě voláním ring 0 kódu z ring 3 přes Call Gate.
Důležité je také vědět, jak CWSDPMI s RAMkou pracuje a jak spolupracuje s dalšími správci paměti. Pokud náš systém (DOS) běží v reálném módu a není zaveden žádný ovladač XMS paměti (himem.sys, himemx.exe, atd.), používá CWSDPMI tzv. raw přístup, kde si veškerou fyzickou RAM obsluhuje sám a pouze v tomto režimu umí využít až 4 GB RAM. Pokud je v paměti zaveden ovladač XMS, tak používá jeho služby přes INT 2Fh. V tomto případě je max. využitelná paměť omezená velikostí největšího souvislého bloku, který XMS ovladač nabízí. Na některých PC je paměť fragmentovaná do více bloků a tak to může být ve výsledku o dost méně než 4 GB. Pokud náš systém běží ve virtuálním módu V86 (pod správou EMM386 či JEMM386, tak je rozhodující, jestli je zapnutá podpora VCPI. CWSDPMI totiž v takovém případě preferuje alokaci paměti přes služby VCPI INT 67h místo služeb XMM. Problém je v tom, že podporovaná velikost VCPI paměti bývá výrazně menší než XMS. U Micro$oftího EMM386 je to pouhých 32 MB (samotný EMM386 má problémy pracovat s více než 1 GB RAM) a u JEMM386 defaultně 128 MB. Také jsem četl o problému, že EMM386 s parametry NOVCPI NOEMS nadále reportuje podporu VCPI (s nulovou velikostí VCPI paměti), což mělo CWSDPMI v tichosti shodit, ale aktuální verze r7 se s tím vypořádá přepnutím na XMS. Zkusil jsem pokusně upravit CWSDPMI v modulu valloc.c (podmínku na řádce 192) tak, aby VCPI ignoroval a používal služeb XMS. Tuto upravenou verzi CWSDPMI r7 NOVCPI nabízím k vyzkoušení. Další problémy můžou nastat u novějších 32-bitových Windows (Vista, 7...), kde je ve výchozím nastavení povoleno pouze 32 MB DPMI paměti. Tento limit lze snadno zvětšit úpravou v registrech. Na svém PC s 4 GB fyzické RAM jsem vyzkoušel různé kombinace DPMI serverů a správců paměti. Testovací program reportoval dostupnou fyzickou DPMI paměť získanou přes funkci _go32_dpmi_get_free_memory_information() v položce struktury _go32_dpmi_meminfo.available_physical_pages, zkusil naalokovat 32 MB nebo méně paměti a spustil další program funkcí system(), který opět reportoval dostupnou paměť DPMI a XMS (dle CWS meminfo.exe). Výsledky jsou v následující tabulce:
XMM EMM/V86MON DPMI server 1st client mem DPMI 2nd nested client mem DPMI/XMS - - CWSDPMI r7 4066 MB 4066 / 0 MB HIMEM - CWSDPMI r7 3506 MB 3506 / 0 MB HIMEM - HDPMI32 3509 MB 3476 / 3538 MB HIMEM EMM386* CWSDPMI r7 31 MB 31 / 981 MB HIMEM EMM386* CWSDPMI r7 NOVCPI 1453 MB 1452 / 0 MB HIMEM EMM386* HDPMI32 957 MB 924 / 905 MB HIMEM JEMM386 CWSDPMI r7 119 MB 119 / 3592 MB HIMEM JEMM386 CWSDPMI r7 NOVCPI 3506 MB 3506 / 0 MB HIMEM JEMM386 HDPMI32 3509 MB 3476 / 3538 MB HIMEM WIN98SE** WIN98SE 1769 MB 1769 / 2048 MB WINXP WINXP** WINXP 2047 MB 2047 / 15 MB * kvůli EMM386 byla XMS omezena na 1 GB, **bylo možno spustit DOS Navigator funkcí system()
ABITVC.EXE ver. 1.0 [48 kB] - Abit BX133-Raid Vcore controll utility je program pro nastavení Vcore bez limitů, tedy v plném rozsahu 1,30 - 3,50 V (zkoušel jsem pouze do 2,00 V). Program mění hodnotu uloženou v CMOS, po nastavení je třeba provést tvrdý reset, aby změna nabyla účinnosti. Více zde.
ACPINFO.EXE ver. 1.0 [43 kB] - vypíše verzi ACPI a seznam nalezených ACPI tabulek.
AWDLS.EXE ver. 1.7 [50 kB] - vypíše informace o komprimovaných modulech v daném Award BIOS image souboru. V podstatě totéž co CBROM /d ale navíc ukáže dekompresní adresy/module IDs a offsety, kde se moduly v image nachazi.
29.3.2008 Do verze 1.2 jsem přidal možnost vykopírovat moduly z image do samostatných souborů, které pak lze snadno rozbalit např. programem LHA 2.55. Také jsem přidal podporu rozeznávání modulů s hlavičkou -lh0- a modulu NCPUCODE, které se objevily v nových Award BIOSech.
11.8.2008 Ve verzi 1.4 jsem opravil počítání kontrolního součtu hlavičky LHA modulů (špatný součet se indikuje znakem '!' za jménem modulu) a chybu při kopírování modulů, které mají nenulovou velikost rozšířené hlavičky (např. MEMINIT.BIN).
9.9.2009 Ve verzi 1.6 jsem opravil nekorektní zpracování modulu NCPUCODE.BIN, nyní ho lze v plné velikosti vyextrahovat.
AWPKICK.EXE ver. 2.0 [53 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - vygeneruje heslo pro Award BIOS 4.51PG do SETUPu. Z původního hesla se v CMOS uchovávají pouze dva bajty, proto ho nelze zjistit, ale jen nahradit sekvencí znaků se stejným kontrolním součtem. Testováno na mojí desce a na školních počítačích P166/233iTX.
BIOS Extract 7.12.2010 [247 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8/Linux + C zdrojáky) - toto je DOSový a WIN32 port linuxové utility BIOS Extract, která slouží pro dekompresi AMI/Award/Phoenix BIOS image souboru na jednotlivé moduly (vše v jednom kroku). Při portování jsem se musel vypořádat s chybějící funkcí mmap(), která je použita pro práci se soubory. Napsal jsem jednoúčelovou náhradu, jenž si alokuje buffer v paměti, kam nakopíruje požadovanou část souboru. Při volání munmap() se pak buffer zapíše do souboru a uzavře.
BMP2EPA.EXE ver. 1.0a [50 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - můj vlastní konvertor formátu *.BMP do *.EPA 2.0. Už mě štvaly různé pitomé sharewary, omezené rozlišením, které mi navíc vždy zdestrojili moji pečlivě připravenou paletu a taxem sám hacknul formát *.EPA. Tento program paletu nemění, takže je třeba se postarat předem o to, aby určité indexy měly správnou barvu (0 - pozadí, 7 - šedý text, 9 - modrý panáček, 15 - bílý text - nelze změnit) zde je paleta pro Paint Shop Pro.
BMP2GRFX.EXE ver. 1.0 [68 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - můj vlastní konvertor formátu *.BMP do *.EPA intel GRFX pro základní desky Asus s AMI BIOSem, které používají tento nový formát loga. Na Asus P5LD2 je velikost obrázku omezena na 640x98/4bit. Tento program nemění paletu a je třeba mít na paměti, že BIOS nahradí určité indexy pro vlastní použití (0 - pozadí, 7 - šedý text, 15 - bílý text).
CODEUP.EXE ver. 1.10 [5 kB] (real mode Borland Pascal executable) Malá utilitka pro nahrání microcode update ze souboru do intel CPU za běhu. Nahraný mikrokód zůstává v procesoru jen do vypnutí nebo restartu PC. Soubor s microcode update pro dané CPUID lze vyextrahovat z image BIOSu (stáhnout od výrobce desky) nebo ze souboru UPDATE.SYS z Windows XP.
COVOXMP3 ver. 1.1 [103 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - přehrávač MP3 s výstupem na D/A převodník Covox na paralelní port. Knihovnu na dekódování MP3 jsem převzal ze Scitech SNAP SDK 3.1. Byla napsaná 100% ANSI C, takže se mi ji povedlo v DJGPP bez problémů přeložit. K tomu jsem dopsal hlavní program MP3 přehrávače, který si na přerušení časovače nainstaluje vlastní p-mode obsluhu přerušení a ta pak posílá přepočtené vzorky na paralelní port. Když je přehrán celý MP3 rámec (1/2 bufferu), nastaví obsluha flag pro hlavní smyčku, která načte a dekóduje další rámec. Mezitím se přehrává druhá polovina bufferu s už dekódovanými daty, takže by se to nemělo nikde zasekávat. Na některých PC jsem pozoroval, že systém nějak nestíhá tak rychlý sled přerušení časovače (např. 44100 Hz) a zvuk se přehrával pomaleji než by měl. Proto jsem přidal možnost vynechání daného počtu vzorků spolu se snížením frekvence časovače a při 22050 Hz se to už přehrávalo OK. Dalším parametrem lze nastavit adresu portu, kam se mají vzorky posílat. Ovládání je jednoduché, šipkami doleva/doprava se přetáčí, nahoru a dolů se mění hlasitost v 8-mi krocích a mezerník je pauza. Program umí načítat i playlist soubory .M3U a zobrazuje ID3 tagy.
CPUID.ZIP ver. 2.24 [144 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8/10/Linux x86/x64) - tento prográmek vypíše info o vašem procesoru (od 386 výše). Nyní umí detekovat i skutečnou a nominální frekvenci jádra, FSB a násobič (pro plnou funkčnost je třeba ho spustit pod DOSem - čtení MSR registrů).
20.3.2005 byla rozšířena CPU databáze o nová jména procesorů intel a AMD a přidán další řádek CPU flagů nových procesorů řady Pentium 4 (SSE3, HT...).
12.3.2007 Nová verze 2.x byla přepsána tak, aby byla na úrovni zdrojového kódu kompatabilní s překladači GCC pro OS DOS, Windows a Linux. Nejprve jsem tedy musel vytvořit knihovnu pro low-level přístup k hardware, která implementuje dané funkce individuálně pro každý OS. V DOSu nebyl žádný problém, přistupuje se přímo. Pod Linuxem se využívá funkce iopl(3), která vyžaduje rootovská práva a zařízení /dev/cpu/?/msr. Pro Windows NT a vyšší jsem musel napsat Kernel Mode Driver a DLL knihovnu, která obsahuje driver a podle potřeby ho nainstaluje/odinstaluje. Inspiroval jsem se projektem ioperm.sys 0.4 Marcela Telky pro Cygwin (po menších úpravách lze přeložit i MinGW32). BTW nová verze Cygwinu 2.6.0 přestala podporovat Windows XP v roce 2016. Moje verze driveru 0.5 je zpětně kompatabilní. Pro Windows XP by driver nebyl nezbytně nutný, neboť lze využít hack na funkci ZwSystemDebugControl z NTDLL.DLL, ale to zas nefunguje ve starších Windows a nemusí to být zaručeno ani po aplikaci nejnovějších záplat. Psaní KMD mi docela stačilo, takže už jsem neměl náladu psát ještě další VXD driver pro Windows 9x. Ty jsou naštěstí dostatečně děravé, takže mi dovolí nainstalovat si vlastní CallGate, přes kterou volám potřebné instrukce RDMSR/WRMSR s oprávněním ring 0. No alespoň jsem se naučil trošku jak funguje chráněný mód 386.
V samotném CPUID programu pak přibyla podpora nových featur procesorů intel, vylepšil jsem detekci velikosti cache, násobiče a FSB a přibyla podpora čtení teploty jader ze senzorů DTS (zde jsou zatím určité nejasnosti, neboť intel mlží s dokumentací a tak na nových procesorech může být údaj chybný, např. CoreTemp 0.94 ukazuje o 15°C nižší teplotu na Core 2 Duo E4300, CPUID by měl ukazovat správně). Dále pomocí parametrů z příkazové řádky můžete přečíst nebo zapsat libovolný MSR registr (použij parametr /h pro help).
26.3.2007 Verze 2.03 opravuje kritickou chybu verze 2.02, kde na starších CPU, které nepodporovaly CPUID extended level info, mohlo dojít k zacyklení programu vlivem špatné detekce CPUID extended levels.
12.5.2007 Do verze 2.06 jsem přidal detekci násobiče pro další CPU od AMD, Cyrix, VIA, IDT a opravil detekci násobiče na P4 a Core. Za potřebné informace a feedback děkuji Janovi Steunebrinkovi.
28.8.2007 Od verze 2.10 (DJGPP) opravena chybná detekce Windows (VDM), která se projevovala např. pod HDPMI32.
16.9.2007 Od verze 2.11 (DJGPP) kompletně přepracován přístup k MSR do ring0, nyní DOSová verze funguje i ve virtuálním módu (např. EMM386) a Win9x.
16.4.2011 Verze 2.14 opravuje nefunkční zápis do MSR přes parametr wrmsr (chyba se pravděpodobně táhne od verze 2.11). Dále byly aktualizovány funkce pro detekci násobiče, FSB, cache, počtu jader, čtení DTS, výpis flagů vybavení, APIC ID mask a databáze jmen procesorů. Program jsem ladil na intel Core i5-750, Core 2 Duo E8400 a Pentium 4 HT.
26.12.2012 Verze 2.15 obsahuje celou řadu vylepšení: podpora neceločíselného násobiče u 45nm procesorů intel Core 2 Duo a detekce turbo násobiče u procesorů intel core i3/5/7, nový výpočet velikosti cache u procesorů intel core i3/5/7, přidán výpis aktuální, min. a max. napětí Vcore (jen pro Core 2 Duo, testováno pouze na 45nm E8500) a byla rozšířena databáze jmen procesorů. Dále jsem rozšířil příkaz wrmsr předávaný z příkazové řádky o další 2 nepovinné parametry bits a shift, pomocí kterých lze pohodlně změnit jen část MSR bez ovlivnění ostatních bitů. Např. pro zapnutí EIST na Core 2 Duo zadej cpuid wrmsr:1A0:1:1:16 - do MSR 1A0h zapíše hodnotu 1, která čítá 1 bit, na pozici bitu 16.
19.1.2013 Do verze 2.16 jsem přidal nový přepínač /s - safe mode, kterým lze explicitně potlačit přístup k MSR a TSC.
7.1.2014 Ve verzi 2.17 jsem opravil chybu detekce FSB u Pentia M, přidal detekci instrukcí AVX2 (Haswell) a aktualizoval databázi jmen procesorů.
26.10.2014 Ve verzi 2.18 jsem aktualizoval databázi jmen procesorů.
16.2.2018 Ve verzi 2.19 jsem opravil detekci násobiče u AMD procesorů rodiny Athlon 64 a detekci velikosti cache. Aktualizoval jsem funkci pro detekci násobiče na procesorech intel core a také rozšířil databázi jmen procesorů.
5.6.2020 Ve verzi 2.20 jsem opravil detekci počtu logických a fyzických CPU jader a přidal výpis detailů turbo-násobičů na CPU intel a také rozšířil databázi jmen procesorů.
11.6.2020 Ve verzi 2.21 jsem opravil drobnou chybku, kdy se na některých CPU neukazovala podpora AVX2, přestože ji měly.
DEMO2.ZIP [55 kB] Pro zajímavost jsem zde uploadnul i jedno 64kB demíčko stvořené kdysi v Borland Pascalu. Bylo dokonce promítnuto na malé lokální demopárty Subway p2k, results roku 1999 v Praze. Jeho náplní je hlavně práce s jednoduchými 3D objekty v prostoru ve VGA módu 13h. Tehdá jsem ho ladil na pentiu 166@200 MHz a vykašlal se na nějakou synchronizaci, takže vám pofrčí asi drobet rychleji :) No dobrá, přeložil jsem ještě jednu verzi s narychlo přidaným Vsyncem.
EDDINFO.EXE ver. 1.1 [10 kB] (real mode Borland C++ executable) Pro otestování schopností rozšířených služeb BIOSu INT 13h pevného disku (BIOS Enhanced Disk Drive services) jsem napsal tento malý prográmek, který zjistí přítomnost a verzi EDD, základní parametry disku a řadiče. EDD definuje nové funkce pro čtení a zápis sektorů, kde umožňuje používat až 64-bitové adresování a tím pádem přístup k obrovské kapacitě. Služby jsou nezávislé na typu a rozhraní paměťového zařízení, ať už je to ATA, SATA, SCSI nebo USB...
EXEINFO.EXE ver. 1.2 [12 kB] - vypíše pár užitečných informací z hlavičky EXE souboru, rozpozná jedná-li se o stub a případně vypíše typ binárního image (LE/NE/PE/COFF/Pharlap...).
1.11.2007 od verze 1.1 detekuje u některých typů image (např. DJGPP COFF, LE) kompresi UPX a vypíše jeho verzi.
12.11.2007 od verze 1.2 rozezná typ image Rational DOS/16M.
FFC.EXE ver. 2.1 [53 kB] - Fast File Comparator, program na binární porovnání dvou (velkých) souborů. Využívá hodně RAM pro cachování. Schválně si jeho rychlost porovnejte se standardním FC v DOSu. Nyní od verze 2.1 podporuje i wildcards a rekurzivní procházení podadresářů.
FOTODATE.EXE ver. 2.2 [66 kB] - jednoduchá utilitka pro nastavení správného datumu a času u souborů JPEG fotografií podle odpovídajících hodnot v EXIFu. Od verze 2.2 podporuje též Motorola (BigEndian) formát EXIFu.
GDTDUMP.EXE ver. 1.1 [35 kB] - okolnosti mě donutily trochu proniknout do principů fungování chráněného režimu 386 a tak jsem si cvičně napsal utilitku na zobrazení obsahu důležitých struktur GDT, IDT a LDT. Vzhledem k různému stupni ochrany v různých OS program funguje pouze v DOSu a Windows 9x. Pod WinNT/2k/XP se mi sice podaří alokovat LDT deskriptor a nastavit ho aby ukazoval na GDT s PL3, ale při jakémkoli přístupu mě OS sestřelí.
GPSMON.ZIP ver. 1.1 & 1.2 [56 kB] je malý prográmek, který jsem si napsal za účelem monitorování příchozích NMEA dat z GPS přijímače připojeného přes sériový adaptér k atárku. Oproti obyčejnému terminálu jsou GPS data zobrazena v přehledné podobě a neutíkají před očima. Podporovány jsou tyto NMEA-183 řetězce: GGA, GGL, GSA, RMC, GSV, VTG a ZDA. Program funguje na Atari Portfoliu a na PC v DOSu. Parametry na příkazové řádce lze nastavit číslo sériového portu a bitovou rychlost. Funkci jsem úspěšně otestoval s GPS modulem Fastrax uPatch100C.
1.9.2007 Přidal jsem verzi 1.1 kompilovanou v DJGPP, která by měla fungovat pod DOSem i Windows. Klávesou D lze přepínat zobrazení zeměpisné šířky a délky DD.dddddd / DD.MM.SS.dd a klávesami + / - lze posouvat lokální časovou zónu. Ukončení klasicky přes ESC.
13.9.2011 verze 1.1 & 1.2 opravuje chybu parseru, která se projevuje, když zeměpisná šířka nebo délka má místo úvodní nuly mezeru, tak program načte vždy 3 číslice pro stupně a tudíž může přečíst i jednu nebo dvě číslice z následujících minut (pokud byly na začátku 1 nebo 2 mezery). Takže např. místo 14°22' se načetlo 142°02. Tato chyba se týkala i programu NMEA2PLT.
ISALL0.EXE ver. 2.0 [82 kB] - Is All Zero-filled File Checker je utilita na skenování souborů, zdali obsahují samé nulové Byty - jméno takového souboru je vypsáno, ostatní soubory jsou ignorovány. Program umožňuje používat wildcards v názvu souboru a případně hledá rekurzivně ve všech podadresářích.
LAPLACE.EXE ver. 1.01 [50 kB] (nepotřebuje CWSDPMI, má CWSDSTUB).
Inspirován předmětem Teorie elektromagnetického pole jsem napsal tento malý prográmek na grafické řešení Laplaceovy rovnice iterační metodou. V první fázi pomocí myši a palety definujete elektrody a jejich potenciál. Defaultní potenciál v okně lze změnit z příkazové řádky /h. Po spuštění se rozjede výpočet, který by měl po nějaké době zkonvergovat ke stabilnímu řešení rozložení potenciálu. Klepnutím na "E" lze zapnout/vypnout zobrazení vektorů intenzity, které však nemusí být úplně korektní.
LIDECR.EXE ver. 1.1 [51 kB] - utilita na dekódování zakryptovaných souborů s příponou .liCrypt, které kóduje neznámý červ/trojan. Moje PC bylo tímto šmejdem napadeno 29.9.2009 ráno (ze staženého freewarového programu Free RapidShare Downloader 1.3), pozná se podle běžícího procesu regdtopt.exe a výrazné diskové aktivity a CPU zátěže. Proces může běžet ve více instancích. Prochází disk a otvírá všechny možné soubory, kterým zakóduje prvních 10 Bytů pomocí statického XOR klíče (naštěstí nejde o destruktivní změnu). Antivir nic nepoznal. Červa jsem dále testoval ve Virtual PC 2007 a zdá se, že klíč nemění. Není ale vyloučeno, že po Internetu koluje více variant s jiným klíčem. V mojem případě to byl řetězec 5C, 34, 1B, 69, DC, AD, 52, 15, 5D, 40. Proto je důležité napřed program otestovat např. na nějakém textovém souboru, kde víme, co mělo na začátku být a pak to teprv pustit na další soubory. Program jsem psal narychlo, takže neumí rekurzivní procházení adresářů, ale jen * wildcardy. Toto omezení lze obejít vytvořením dávkového souboru, který vytvoříme z výpisu souborů na disku např. příkazem dir C:\* /b /on /s >list.bat a následnou úpravou hledat/nahradit v editoru, kde přidáme volání LIDECR.
30.9.2009 Jak jsem záhy zjistil, tak červ používá pro různé typy souborů různou délku klíče. Obvykle to je 10 Bytů, ale u souborů .TXT, .DOC, .XLS to může být 35 Bytů. Celý klíč je řetězec: 5C 34 1B 69 DC AD 52 15 5D 40 C5 90 C2 B8 06 33 AD E0 23 0D FC A3 20 C1 82 BA E7 A3 0B D0 C9 E0 C9 F7 3F. Do verze 1.1 jsem přidal povinný parametr, kterým se volí použitá délka klíče [1 - 35 Bytů].
MCOPY.ZIP ver. 1.25 [81 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8) - tato drobná utilita vám umožní ze vstupního souboru vykopírovat jakoukoliv část do jiného souboru. Stačí zadat offset a velikost bloku. Od vereze 1.23 akceptuje též hexadecimální argumenty označené prefixem 0x. Od verze 1.25 může být vstupní soubor shodný s výstupním (napřed se vytvoří dočasný soubor a ten se pak přejmenuje).
MP3CUT.EXE ver. 1.0 [52 kB] - jednoduchý nástroj na bezeztrátové stříhání MP3 souborů. Umožňuje ze začátku a z konce souboru odstranit daný počet vteřin (když je tam příliš dlouhá pauza nebo nějaký rušivý lupanec), aniž by bylo potřeba soubor rekompresovat. Pozor, při oříznutí začátku souboru je odstraněn i ID3 v2 tag a při oříznutí konce souboru je odstraněn ID3 v1 tag. Program by měl pracovat s jakýmkoliv MPEG audio souborem různých vzorkovacích frekvencí a bitrate. Zatím jsem ho testoval jen na MPEG1 layer 3 souborech.
MINJECT.ZIP ver. 1.1 [74 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8) - provádí v posdtatě opačnou operaci než MCOPY - na určenou pozici v cílovém souboru zapíše data ze zdrojového souboru dané délky. Akceptuje též hexadecimální argumenty označené prefixem 0x.
MTRRLFBE.EXE ver. 1.6 [62 kB] - MTRR-WC enabler for VESA LFB. Na žádost jsem napsal tento malý prográmek, který umožňuje samostatně nastavit režim MTRR registrů pro oblast VGA (A0000h) a LFB (adresu si detekuje sám). Lze tak při nastavení Write-Combining režimu urychlit stávající programy, které vykreslují přes VESA VBE. Na svém stroji Celeron Tualatin 1466, intel BX chipset, SVGA GeForce MX440 jsem např. v režimu 800 x 600 / 32 LFB dosáhl zvětšení propustnosti (RAM->VRAM) z 62 MB/s na 315 MB/s.
BTW celá věc ohledně MTRRLFBE začala takto: když jsem programoval nějaké grafické funkce v DOSu pro svojí knihovnu, prováděl jsem taky benchmarky různých algoritmů abych vybral ten nejrychlejší. Občas se ale stalo, že program proběhl třeba 5x rychleji než normálně. Ale nesouviselo to nijak s mým kódem, prostě všechny grafické programy v DOSu byly najednou rychlejší. Vysledoval jsem, že tato situace nastane, když restartuju z Win98 přímo do DOSu, aniž by probíhal POST BIOSu. To mě vedlo k myšlence, že nejspíš samy Windows nebo nVidia ovladače něco nastavují tak, že kopírování do framebufferu je mnohem rychlejší. Nakonec jsem vygooglil tento dokument intelu, který popisuje nastavení MTRR a jejich vliv na výkon grafiky. Pak už bylo docela snadné napsat kód, který MTRR nastaví.
8.1.2007 Verze 1.1 dovoluje nastavit vlastní rozsah adres pro daný režim pomocí parametru USER:bazova_aresa:velikost_kB.
10.3.2011 Ve verzi 1.3 jsem kompletně přepsal funkce pro nastavení MSR registů tak, aby fungovaly i při běhu pod starým správcem paměti emm386.exe a ve Windows 9x.
11.11.2018 Do verze 1.5 jsem přidal kód, který analyzuje a případně modifikuje existující nastavení MTRR tak, aby v případě překrývajících se oblastí bylo možno nastavit prioritně Write-Combining režim. Pokud máte přesto pocit, že změna nastavení nepřinesla kýžený efekt, zkuste program spustit s parametrem /d, který vypíše stávající nastavení všech MTRR a modifikace, jenž program provádí a tento log mi pošlete. Tuto verzi jsem úspěšně otestoval na nVidia GeForce GTX670 v základní desce Gigabyte GA-P67-DS3-B3. Dosáhl jsem přenosové rychlosti 2847 MB/s.
29.12.2018 Důležité upozornění pro používání MTRRLFBE ve virtuálním režimu v86: zjistilo se, že u některých starších programů spuštěných ve v86 režimu nedojde po aplikaci MTRRLFBE pro nastavení LFB do režimu WC k žádnému zrychlení, zatím co pokud se tytéž programy spustí v reálném režimu, tak ke zrychlení dojde. Já a uživatel Falcosoft z VOGONS fóra jsme toto chování ověřili na více PC konfiguracích (od Pentia Pro po Core i7 2600K), takže nejde o náhodu. Podařilo se mi problém zúžit na starší programy, které používají typicky extender DOS/4GW (např. hry Blood, Duke Nukem 3D a benchmarky perf, profile). Naproti tomu novější programy kompilované v DJGPP, které používají externí DPMI server (CWSDPMI) fungují ve v86 režimu správně a dostaví se u nich výrazné zrychlení. To zahrnuje např. můj program VESATEST a moderní herní enginy QDOS, Q2DOS a Hexen II. Zatím netušíme, co je příčinou tohoto problému. Možná to nějak souvisí s funkcí mapování fyzické adresy na logickou adresu v programu. Případné řešení může vyžadovat modifikaci DOS extenderu nebo správce paměti.
UPDATE: pokud se zavede HDPMI jako rezidentní DPMI server (pomocí příkazu hdpmi32.exe -r -i), tak se zrychlí i starší programy využívající DOS/4GW extender. Pokud se pak zavede rezidentní emulátor Yamaha dsdma.exe (pro PCI zvukové karty Yamaha 7xx), tak se zas efekt zrychlení anuluje.
15.1.2021 Do verze 1.6 jsem přidal nový parameter info, který pouze vypíše konfiguraci MTRR bez jakékoliv změny pro účely ladění.
NMEA2PLT.EXE ver. 1.3 [53 kB] - konvertor surových GPS dat NMEA-183 do formátu stopy pro OziExplorer. Z terminálového logu vybere řetězce GGA s validním fixem a uloží je jako body stopy ve formátu .PLT. Tento soubor pak lze načíst a zobrazit v OziExploreru, nejlépe na podkladu kalibrované mapy. Časové údaje se uvažují v UTC, OziExplorer je sám zobrazí v LTC, ale pomocí proměnné prostředí TZ je možno provést korekci už při převodu.
23.1.2007 verze 1.1 opravuje chybu, kdy v případě pěti desetinných míst u zeměpisné šířky a délky docházelo k přetečení a chybným výsledkům v desetinné části stupňů. Dále byla opravena položka udávající počet waypointů v souboru, aby odpovídala skutečnosti (program vynechává NMEA řetězce s neplatným fixem).
5.9.2007 verze 1.2 upravena z BC pro DJGPP, bylo vylepšeno filtrování vět, takže si poradí i s různým binárním bordelem jako třeba UBX pakety mezi textovými větami. Také jsem přidal počítání kontrolního součtu vět, takže porušené věty jsou ze zpracování vypuštěny.
13.9.2011 verze 1.3 opravuje chybu parseru, která se projevuje, když zeměpisná šířka nebo délka má místo úvodní nuly mezeru, tak program načte vždy 3 číslice pro stupně a tudíž může přečíst i jednu nebo dvě číslice z následujících minut (pokud byly na začátku 1 nebo 2 mezery). Takže např. místo 14°22' se načetlo 142°02. Tato chyba se týkala i programu GPSMON. Stejným neduhem trpí i program OziExplorer File Format Converter 1.13.
NVSC.COM ver. 1.0 [1 kB] (realmode COM + NASM zdroják) - malá utilitka na přepínání režimu on-chip scaleru grafických karet nVidia pro případ připojení LCD monitoru přes DVI-D. To se může hodit pokud požadujete neinterpolovaný výstup nižších rozlišení 1:1.
NVCLOCK.ZIP ver. 0.8 beta 4 [406 kB] (DOS/Win9x/NT/2k/XP + C zdrojáky) - toto je DOSový a WIN32 port známého linuxového nástroje NVClock. pro grafické karty nVidia. Umožňuje nastavit taktovací kmitočet GPU a videopaměti, zapínat pixel pipeliny, řídit otáčky ventilátoru na GPU, číst teplotu GPU a výpis technických informací včetně MMIO registrů. Win32 port už sice ve zdrojových kódech existoval, ale binár jsem nikde nepotkal. Navíc byla tato sekce kódu zastaralá a zanedbaná a configure dokonce odmítal vytvořit mejkfajly. Nakonec se mi podařilo vše opravit a zkompilovat. K běhu budete ještě potřebovat nainstalovat knihovnu MemAccess Library 1.4 pro přímý přístup k fyzické paměti (MMIO registrům GPU). Jako správný fanda DOSu jsem taky nemohl odolat pokušení portnout NVClock i pro DOS. Pro přímý přístup k fyzické paměti používám DPMI funkci __djgpp_map_physical_memory(), která ale vyžaduje DPMI 1.0 server nebo podporu funkce 0508h. V případě CWSDPMI není žádný problém.
2.3.2009 jsem rozšířil NVClock o bohatší debug výpis (přidány další registry, zejména RAMDAC CRTC) a přidal jsem novou funkci -e, --expansion <mode>, která umožňuje nastavit scaling obrazu pro připojený LCD panel přes DVI-D. Lze vybrat režim roztažení na celou plochu, obraz 1:1 uprostřed a nativní režim (bez přepočítávání) - ten mi ale funguje pouze v rozlišení 1280x1024 (při menších rozlišeních jde do monitoru nesmyslná frekvence a ten skončí s hláškou out of sync). Možná je to problém jen mojí grafické karty Asus EN7900GT/2DHT.
PI.EXE ver. 1.1 [60 kB] - prográmek na výpočet čísla π na 64k číslic - jednoduchý benchmark (optimalizováno pro Pentium III). Na mém přetaktovaném stroji Core 2 Duo E8600 @4GHz trvá výpočet 10,48 s.
PWRDOWN.EXE ver. 1.1 [44 kB] - jednoduchá utilita na vypnutí PC s ATX zdrojem. Funguje pouze na základních deskách s jižním můstkem intel ICH nebo PCH.
RAW2C.EXE ver. 1.3 [48 kB] - malý pomocník C programátora, převede jakýkoliv soubor do textového souboru s definicí a inicializací proměnné bajtového pole, který lze pak inkludovat do jiného zdrojového .C souboru.
RAWSPEED.EXE ver. 2.3 [75 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - jednoduchý diskový benchmark. Zjistí průměrnou zápisovou a čtecí rychlost.
ROTOZOOM.EXE ver. 2.2 [59 kB] (nepotřebuje CWSDPMI, má CWSDSTUB).
Rotozoomer - prográmek na real-time rotaci a zoomování obrázku. Impuls k napsání tohoto prográmku mi dal kolega, který totéž psal v assembleru jako semestrálku, ale jeho kód, využívající matematický koprocesor, zrovna rychlostí neoslnil. A tak jsem napsal svoji verzi v C, zvědav jak si s tím poradí optimalizace GCC. Na mojem PC byl program asi 5x rychlejší, na Pentiu 4 dokonce asi 20x. Umožňuje načítat GIFy a 8-bit TGA (i RLE), teoreticky omezené jen velikostí DPMI paměti. Zde je jeden - face.gif.
SERREN.EXE ver. 1.0 [56 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - malá utilitka, která zprovozní propojení SERR# signálu z PCI sběrnice na NMI procesoru nastavením routingu v chipsetu intel ICHx. U některých základních desek / BIOSů toto propojení nefunguje, což brání funkčnosti DOSových driverů SoundBlaster Live! To je sice podmínka nutná, ale nikoliv postačující (na mojí desce GA-P31-DS3L po zapnutí routingu začal fungovat MIDI a Adlib zvuk, ale SFX stále mlčí - patrně problém s DMA).
SMB.ZIP ver. 2.09 [221 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8/Linux).
Nyní se zabývám komunikací po sběrnici SMBus, která je v každém novějším počítači a umožňuje pomocí SMB řadiče komunikovat s různými zařízeními základní desky jako např. hodinovým obvodem PLL (mj. řídí FSB), SPD EEPROM na modulu DIMM či různými monitorovacími obvody. Problém je, že každá řada chipsetů má jinak implementován řadič SMBus a proto můj program podporuje pouze základní desky s obvodem southbridge intel 82371 (PIIX4) vyskytujícím se na deskách s chipsety intel TX,LX,BX,ZX,GX pro který jsem měl možnost jej odladit. Stejně tak každé SMB zařízení má svoje specifické ovládání a registry.
Od 10.1.2003 je v programu zahrnuta experimentální podpora southbridge intel 82801 (ICHx), takže by to mělo chodit i na novějších chipsetech intel 8xx.
Program zatím umí číst informace o modulech DIMM (včetně výrobce, velikosti, přístupové doby...), číst a nastavovat frekvenci FSB a spread spectrum u PLL obvodů Winbond 83194R-02/04/39/39A, 83195R-08, ICS 9148-26 a CY 28349 a číst teplotu z obvodů MAX1617 a LM75. Testoval jsem jej pouze na obvodu Winbond 83194R-39A. Program používám na "adaptivní overclocking", kdy přetaktuji pouze v případě, že spouštím nějaký náročný program, jinak není důvod 850MHz procesor zbytečně vařit ;-).
4.7.2003 verze SMB 1.13 obsahuje několik podstatných novinek:
-otestovaná podpora southbridge ICH až ICH5 (ICH6) -informace o modulech DIMM jsou nyní korektní i pro paměti DDR, aktualizovaná databáze výrobců (JEDEC ID)
-podpora PLL Cypress CY28349 s krokem FSB po ~1 MHz od 50 do 248 MHz
-program SCANSMB je nyní integrován do SMB a je dostupný pod parametrem /s
12.2.2005 do verze SMB 1.15 byla přidána podpora pro obvod-HW monitor ADT7463 a experimentální podpora pro PLL Realtek RTM520-39D (desky Abit BX133 a BF6). FSB lze zatím nastavovat jen v 8-krocích, protože nevím jak odemknout další registry PLL obsahující M,N faktory pro jemné nastavování po 1 MHz, takže zatím aspoň něco. Také byla aktualizována databáze ID výrobců (JEDEC ID).
18.2.2005 od verze SMB 1.17 budou k dispozici dvě verze programu. Jedna klasicky pro DOS a Win9x kompilovaná v DJGPP a druhá pro Win9x/NT/2K/XP kompilovaná v MinGW32. Windowsácká verze by nemohla vzniknout bez projektu IO.DLL, což je systémová knihovna umožňující přímý přístup k I/O portům pomocí klasických funkcí typu inport/outport. Knihovna se nemusí nijak instalovat, stačí aby byla někde v systémové cestě a program by si ji měl natáhnout sám. Vcelku nenáročnou úpravou svého zdrojáku jsem dosáhl toho, že program můžu kompilovat beze změny oběma překladači (využití #ifdef __MINGW32__ ..., a náhrada pár funkcí z conio.h pomocí funkcí WinAPI).
Nová verze dále obsahuje plnou podporu HW monitoru Winbond W83782D, který je na základních deskách celkem rozšířený. Novinkou je také experimentální debug konzole, kterou lze spustit SMB /dbg. Pomocí zadávaných příkazů lze volat interní C-funkce na obsluhu PCI a SMB a tímto způsobem komunikovat s dosud nepodporovanými zařízeními. Díky skvělé možnosti směrování I/O v C lze příkazy zapsat do textového souboru a pak je spustit jako skript SMB /dbg skript.txt. Jen upozorňuji, že funkce je třeba používat velmi opatrně, jedním chybným zápisem lze zatuhnout počítač tak, že nepomůže ani tvrdý reset nebo i poškodit hardware.
5.3.2005 Do nové verze SMB 1.18 byla implementována podpora IDE/ATA. Pomocí parametru SMB /ide [hexdump] lze získat spoustu zajímavých informací o IDE discích, včetně S.M.A.R.T. statistiky a třeba i teploty disku, pokud to podporuje (prakticky všechny nové disky). Program funguje i s ternárním a kvaternárním IDE kanálem řadiče HighPoint HPT37x, což ne každý podobný program rozdejchá (bohužel jen v DOS/W9X, NT-suxx). Patřičně byla také rozšířena paleta funkcí konzole, lze například nastavovat AAM (Automatic Acoustic Management), APM, vypnout motor jednotky, číst/zapisovat sektory, měnit konfiguraci... V podstatě lze provést jakýkoliv příkaz ATA, parametry je potřeba nastudovat v příslušné dokumentaci (ATA-7a, ATA-7b, ATA-7c). Pokud příkaz čte/zapisuje nějaká data, jsou uchována v 512B bufferu, který lze modifikovat, mazat a vypisovat. Jako ukázku, jak využít možnosti konzole, jsem napsal tento demo skriptík: smbdemo.txt, který se spustí SMB /dbg smbdemo.txt.
5.6.2007 Nová řada SMB 2.xx byla z velké části přepsána s využitím knihovny/driveru ioperm.dll 0.5 (stejně jako u programu CPUID) pro Windows řady NT a nově vznikla i verze pro Linux. Přepsány byly moduly obsluhy PCI, SMBus - kde bylo vylepšeno časování a zrychlena komunikace a také přibyla podpora posledních chipsetů ICH7, ICH8 a 631x/632x od intelu, IDE - kde přibyla podpora IDE/ATAPI řadiče ITE8211/8212 a taktéž ICH7, ICH8 a ve výpisu zařízení nyní zobrazuje informace i o ATAPI mechanikách. Podpora IDE je pro Windows řady NT zatím experimentální, vzhledem k nemožnosti zakázat přerušení doporučuju spouštět jen pokud systém zrovna neprovádí diskové operace (kontrolka HDD nesvítí), otestováno na ITE2811 a HPT370 pod Windows XP. Modul SPD byl rozšířen o podporu DDR II pamětí a doplněna databáze výrobců (JEDEC ID). Nově také přibyla funkce PCI scanneru, která se spustí parametrem SMB /pci a vypíše informace o všech nalezených PCI zařízeních. Program má už v sobě vestavěnou malou databázi PCI vendor ID, ale pokud chcete podrobnější informace (i s názvem konkrétního zařízení), tak doporučuji stáhnout velkou databázi z http://members.datafast.net.au/dft0802, kterou řada lidí průběžně doplňuje (provoz ukončen). Soubor PCIDEVS.TXT stačí nakopírovat do stejného adresáře odkud je SMB.EXE spouštěn a program už sám zdetekuje jeho přítomnost a bude v něm vyhledávat. Také se změnily některé příkazy v debug konzoli.
16.9.2007 Verze 2.05 přináší experimentální podporu intel ICH9 a opravuje chybu linuxové verze, kdy program spadnul při pokusu uložit DIMM SPD data do souboru.
16.3.2008 Do verze 2.06 jsem přidal nově podporu pro HW monitor, který je součástí LPC SuperIO Winbond W83627. Pomocí parametru SMB /t fan_type pwm_duty_cycle, kde fan_type je 'a'-auxfan|'c'-cpufan|'s'-systemfan a pwm_duty_cycle je číslo [0-255] určující střídu výstupního signálu PWM regulátoru, lze plynule nastavit otáčky větráku. Také jsem aktualizoval databázi výrobců (JEDEC ID).
23.3.2008 Verze 2.07 přináší experimentální podporu PLL ICS 954128. Lze nastavit 23 fixních kroků FSB v rozsahu 100 - 400 MHz. Bohužel ale na mojí desce P4LA i při malé změně FSB došlo k výpadku funkce SATA nebo zakousnutí celého systému, takže nemám dále možnost tuto funkci ladit. Také jsem provedl optimalizaci PLL kódu, díky čemuž je nová binárka o chlup menší.
8.4.2008 jsem přidal do verze 2.08 novou funkci, která umožňuje spolu s parametrem /pci [file] použití externího textového PDT (PCI Device Template) souboru šablony. V tomto souboru lze nadefinovat PCI registry v rámci jednoho PCI zařízení spolu s popisky a formátovacím řetězcem jak se má hodnota vypisovat a přizpůsobit si tak výpis na míru podle daného zařízení. Na ukázku jsem do balíčku přidal šablonu pro intelí (G)MCH 82P31.
7.9.2009 jsem přidal do verze 2.09 podporu pro SMBus řadič ICH10(R). Úspěšně otestováno na desce Gigabyte GA-P43-ES3G.
Pokud program vyzkoušíte, dejte mi laskavě vědět, jak vám chodí a s jakým PLL. Budu vděčen i za poslání vašeho logu o modulech DIMM (SMB /d >dimm.log). Zde je ukázka logu mojeho DIMMu 128MB PC133, SMB scanu a HW monitoru. Pokud máte na desce jiný PLL obvod který není podporován a máte zájem o použití programu, stačí napsat a pokud budu mít čas pokusím se jej implementovat.
SMINFO.ZIP ver. 1.2 [76 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8/10/Linux x86/x64).
Trochu jsem zkoumal, jak funguje SMM (System Management Mode) a SMI (System Management Interrupt) a tak jsem si napsal jednoduchý prográmek, který z chipsetu a MSR vytahá pár zajímavých informací, např. jestli je SMRAM po bootu ještě odemčená - bit D_LCK (a umožňuje tak teoreticky nainstalovat SMI rootkit) či kolikrát bylo SMI od resetu aktivováno. Program je určený pouze pro chipsety a CPU intel řady P4/M - Core (na starších platformách nejsou dostupné některé informace jako třeba SMI trigger counter).
13.4.2020 Do DOSové verze 1.1 jsem přidal možnost uložit obsah paměti SMRAM do souboru (pokud je odemčená) pomocí parametru /d filename. Podařilo se mi úspěšně dumpnout SMRAM na notebooku Compaq EVO N620c a začal jsem ji zkoumat v IDA disassembleru. Oproti předpokladům se mi zdá, že entrypoint leží někde jinde, než na adrese A8000h. Spíše to vypadá, že zajímavý kód je až v oblasti od B8000h, která začíná řetězcem !$SMM MODULE -- COMPAQVERSION: 1.0 a obsahuje celkem 4 instrukce RSM (opcode 0FAAh) - návrat ze SMM.
20.4.2020 Do nové verze 1.2 jsem přidal možnost SMRAM permanentně zamknout nahozením bitu D_LCK pomocí parametru /l.
SPIPGM.ZIP ver. 2.35 [130 kB] (DOS/Win9x/NT/2k/XP/Vista/7/8/10/Linux x86/x64) je software určený k programování sériových SPI FlashROM pamětí připojených k PC přes paralelní port. Umožňuje identifikovat, číst, programovat, verifikovat, mazat a odemykat paměť. Více informací o potřebném hardware zde.
9.6.2008 Přidána detekce FlashROM pamětí SST25VFxxx a SST26VFxxx. Nemám žádnou paměť na které bych to vyzkoušel, ale podle datasheetu by to mělo fungovat.
1.8.2008 Přidána detekce FlashROM pamětí Macronix MX25L1005/1006E, MX25L2005/2006E, MX25L4005/4006E, MX25L8005/8006E - otestováno, MX25L1605/1606E - otestováno, MX25L3205/3206E, MX25L6405/6406E/6435E/6445E/6473E - otestováno.
25.8.2008 Přidána detekce FlashROM pamětí Atmel AT26DF041 - AT26DF321 - netestováno.
28.10.2008 Přidána detekce FlashROM pamětí Winbond W25X16 - W25X64 - otestováno s W25X32 a W25X80.
10.1.2009 Přidána detekce FlashROM pamětí Spansion S25FL004A - S25FL128P - otestováno s S25FL032A a S25FL064A/P.
21.1.2009 Přidána detekce FlashROM pamětí intel QB25F016S33B8, QB25F032S33B8, QB25F064S33B8 - netestováno.
11.10.2010 Přidána detekce FlashROM pamětí EON EN25F16, EN25F32, EN25F64, EN25F128 - netestováno. Také jsem vylepšil funkce ovládání LPT portu, takže čtení pamětí by mělo být nyní asi 1,6x rychlejší.
22.12.2010 Opravena detekce FlashROM pamětí intel a přidána detekce pamětí AMIC A25L05PU/PT, A25L10PU/PT, A25L20PU/PT, A25L40PU/PT, A25L80PU/PT, A25L16PU/PT, A25L32PU/PT, A25L64PU/PT, A25L512, A25L010, A25L020, A25L040, A25L080 - otestováno s A25L080. Také jsem přidal kontrolu parity JEDEC ID Byte, což by mělo pomoci hned na začátku odhalit problém s komunikací (špatný kabel, nevhodné napájení, atp.).
31.1.2011 Přidána detekce FlashROM pamětí EON EN25F20, EN25F40, EN25F80 - otestováno s EN25F80.
5.2.2011 Přidána detekce FlashROM pamětí Winbond W25Q10, W25Q20BV, W25Q40BV, W25Q80BV - otestováno s W25Q80BV.
22.2.2011 Přidána detekce FlashROM pamětí Atmel AT26F004 - otestováno a ESMT F25L004A - otestováno, F25S04PA, F25L08PA, F25L16PA, F25L32PA, F25L32QA.
20.3.2011 Přidána detekce FlashROM pamětí Winbond W25Q16BV - otestováno, W25Q32BV - otestováno, W25Q64BV, W25Q128BV; PMC Pm25LV512A - otestováno, Pm25LV010A - otestováno, Pm25LV020, Pm25LV040 - otestováno, Pm25LV080B, Pm25LV016B, Pm25LV032B, Pm25LV064B.
30.1.2012 V nové verzi 2.0 jsem přepsal velkou část kódu. Zásadně byla přepracována funkce na detekci FlashROM pamětí zvětšující flexibilitu. Nyní jsou podporovány i některé starší paměti, které nemají identifikaci přes JEDEC ID, ale mají kompatabilní ostatní příkazy. Programovací funkci jsem rozšířil o algoritmus Auto Address Increment (AAI) Byte/Word programming mode, který používají starší FlashROM od SST a ESMT. Děkuji Frantovi Ryšánkovi za poskytnutí paměti SST25VF080B na které jsem to mohl odladit. Pro paměti ST řady M45PExx jsem musel přidat funkci mazání po 64kB blocích, protože tyto paměti nemají příkaz Chip Erase. Dále jsem přidal příkaz verify, který porovná obsah paměti se souborem. Nyní je také možno flashovat i image soubory, jejichž velikost je menší než velikost paměti. Program vypíše varování, ale dovolí normálně pokračovat. Nakonec jsem upravil i low-level funkce pro ovládání pinů SPI na základě doporučení Helge Wagnera, díky čemuž se čtení paměti zrychlilo o 25% a zápis o 41%. Přidána detekce FlashROM pamětí Atmel AT25F512B, AT25DF021, AT26DF161, AT25DQ161, AT25DQ321A; EON EN25B10, EN25B20, EN25B40(T), EN25B80, EN25B16; ESMT F25L008A/08PA, F25L016A/16PA, F25L32PA, F25L64PA, F25L16QA; Macronix MX25L512E, MX25L12835E/12836E, MX25L25635E/25735E/25835E; Spansion S25FL256S, S25FL512S, S25FL01GS; ST/Numonyx M25P05, M45PE10, M45PE20, M45PE40, M45PE80, M45PE16, M25PX80, M25PX16, M25PX32, M25PX64, N25Q032A13E, N25Q032A11E, N25Q064A13E, N25Q064A11E, N25Q128A13E, N25Q128A11E, N25Q256A13E, N25Q256A11E, N25Q512A13G, N25Q512A11G, N25Q00AA13GB; SST25VF512(B) - otestováno, SST25VF010(B), SST25VF020(B), SST25VF040(B), SST25VF080(B) - otestováno, SST25VF016(B) - otestováno, SST25VF032(B), SST25VF064C, SST25VF128(B); Winbond W25Q256F.
7.2.2012 Přidána detekce FlashROM paměti ST M25P10AV - otestováno.
19.4.2012 Přidána detekce FlashROM pamětí GigaDevice GD25Q512, GD25Q10, GD25Q20, GD25Q40, GD25Q80, GD25Q16 - otestováno, GD25Q32 - otestováno a GD25Q64.
8.4.2013 Opravena chyba detekce FlashROM pamětí Spansion S25FL128P, S25FL128S, S25FL129P, S25FL129S pro které jsem měl v tabulce špatné JEDEC ID (překlep).
14.4.2013 Přidána detekce FlashROM pamětí EON EN25Q40, EN25Q80, EN25Q16, EN25Q32, EN25Q64, EN25Q128 - netestováno.
27.6.2013 Přidána funkce pro kontrolu výmazu FlashROM paměti (parametr /b).
3.7.2013 Přidána detekce FlashROM pamětí PMC Pm25LD512 - otestováno, Pm25LD010 - otestováno a Pm25LD020.
15.8.2013 Přidána funkce pro zápis stavového Byte do FlashROM (parametr /s status). Pomocí BP a SRP bitů lze nastavit ochranu proti zápisu spolu v součinnosti s pinem WP#. Tyto bity mohou být specifické podle typu paměti, viz datasheet.
17.8.2013 Přidána detekce FlashROM pamětí EON EN25T10, EN25T20, EN25T40, EN25T80, EN25T16, EN25T32, EN25T64 - netestováno.
1.10.2013 Přidána detekce FlashROM pamětí EON EN25QH16, EN25QH32 - otestováno, EN25QH64, EN25QH128, EN25QH256.
2.1.2014 Do nové verze 2.18 jsem přidal podporu pro čtení/zápis rozšířeného status registru (některé novější FlashROM ho mají 16-bitový), ve Win32 verzi jsem přepsal funkci udelay() pro přesnější časování (při použití volby /d=), přidal jsem novou volbu /o= pro nastavení počátečního stavu datových linek D7:0 LPT portu a zkusil jsem pokusně přeložit 64-bitovou binárku pro Linux.
3.5.2014 Do nové verze 2.19 jsem přidal podporu pro čtení unikátního 64b sériového čísla z novějších flashek Winbond a detekci nových 1,8V Winbondů W25Q16FW, W25Q32FW, W25Q64FW, W25Q128FW - netestováno. Pro tyto paměti je nutné vyrobit složitější programátor s LDO a nějakým level translátorem, který upraví napěťové úrovně z/do LPT. Tyto obvody vyrábí např. Texas Instruments. Později jsem sám potřeboval naprogramovat 1,8V paměť a tak jsem zbastlil tento jednoduchý převodník.
25.9.2014 Přidána detekce FlashROM pamětí Macronix MX25L5121E, MX25U5121E, MX25L1021E, MX25U1001E, MX25U2033E, MX25U4033/4035/25V4033/4035, MX25U8033/8035E, MX25V8035, MX25L1633/1635/1636/1673/1675E, MX25L1635/1636E, MX25U1635, MX25U3235/25L3239E, MX25L3225/3236/3237D, MX25U6435/25L6439E - otestováno, MX25L12835/12836/12839/12845/12865/12873/12875F - otestováno, MX25U25635F, MX66L51235F/51245G, MX66U51235F, MX66L1G45G - netestováno.
26.10.2014 Do nové verze 2.21 jsem přidal experimentální podporu 32-bitového adresního režimu pro paměti větší než 16 MB. Nyní by mělo být možno u větších pamětí přečíst/naprogramovat celou kapacitu, bohužel však žádnou takovou paměť na otestování nemám.
POZOR, tato verze SPIPGM obsahuje závažnou chybu v programovacím příkazu, updatujte na novější!
4.12.2014 Verze 2.22 opravuje chybu v programovacím příkazu z předešlé verze 2.21 a byla přidána detekce FlashROM paměti PMC/Chingis Pm25LQ032C - netestováno.
6.2.2015 Verze 2.23 opravuje chybu ve funkci pro odemykání a zápis do status registru (chybné pořadí příkazů WREN a EWSR) u pamětí typu SST25VFxxx. Úspěšně otestováno na SST25VF010A, děkuji Břéťovi za zaslání vzorku.
15.2.2017 Verze 2.24 opravuje chybu ve funkci pro odemykání novějších pamětí Atmel. Problém byl v tom, že Atmel má jinak definované bity ve status registru. Zatím co u většiny výrobců má bit5 význam BP3 (Block Protection bit3) a jeho výchozí hodnota je 0, tak u Atmelu má bit 5 význam WPP (Write Protect Pin), který reflektuje úroveň na pinu WP# a měl by mít vysokou úroveň. Funkce pro kontrolu odemknutí paměti tak vyhodnotila, že se odemčení nezdařilo a program skončil s chybou. Odemykání jsem otestoval na pamětech AT26DF161 a 26DF081A. Také jsem opravil chybu detekce paměti EON EN25B40.
27.2.2017 Do nové verze 2.25 jsem přidal kód pro výpis status bitů specifických pro výrobce pamětí GigaDevice.
5.9.2017 Do nové verze 2.26 byla přidána detekce FlashROM pamětí GigaDevice GD25Q127/25Q128, GD25Q256C, GD25Q512MC, GD25VQ20C/25VQ21B, GD25VQ40C, GD25VQ80C, GD25VQ16C, GD25VQ32C, GD25VQ64, GD25VQ127C, GD25LQ05B, GD25LQ10B, GD25LQ20B, GD25LQ40B, GD25LQ80B, GD25LQ16, GD25LQ32, GD25LQ64 - otestováno, GD25LQ128, GD25LQ256 - netestováno.
12.12.2017 Do nové verze 2.27 jsem přidal experimentální podporu 32-bitového adresního režimu pro paměti Spansion větší než 16 MB. Nemám ale žádnou takovou paměť na vyzkoušení.
25.5.2018 Do nové verze 2.28 jsem přidal kód pro výpis status bitů specifických pro výrobce pamětí Winbond a upravil odemykací funkci tak, aby vynulovala i druhý stavový Byte (pokud ho paměť má).
5.10.2018 jsem portoval svůj kernel mode driver na 64-bitová Windows (konkrétně jsem ho testoval na Windows 10). Při tom mi hodně pomohl prohlížeč ladicích výpisů DebugView 4.81. Ovladač není digitálně podepsán, takže musíte při bootu vypnout vynucení digitálního podpisu ovladačů. Pokud už jste dříve pod Windows SPIPGM spouštěli, doporučuji smazat starou verzi driveru IOPERM.SYS z adresáře %WINDIR%\SYSTEM32\DRIVERS. Nelze-li smazat, zkuste v příkazovém řádku spustit příkaz: net stop ioperm05 a pak znovu smazat. Po spuštění nové verze SPIPGM se automaticky vybalí nová verze ovladače.
18.10.2019 Do nové verze 2.29 byla přidána detekce FlashROM pamětí ISSI IS25LE/LP/LQ/WE/WP/WQxxx - netestováno.
10.3.2020 Do nové verze 2.30 byla přidána detekce FlashROM pamětí Fudan FM25Fxxx, FM25Qxx - otestováno na FM25F02A.
3.1.2021 Do nové verze 2.31 byla přidána detekce FlashROM pamětí ON Semiconductor LE25W81QE, LE25Uxx, LE25Sxxx - otestováno na LE25S81MC a Winbond W25QxxBW - otestováno na W25Q80BW. Také jsem opravil bit protection masky ve statusu u mnoha pamětí a zpřesnil výpis 2B statusu u pamětí Winbond.
7.2.2021 Do nové verze 2.32 byla přidána detekce FlashROM pamětí Bright Moon Semiconductor T25Sxx, H&Msemi HM25Qxx a Zbit Semiconductor ZB25Dxx, ZB25LDxx, 25WDxx, ZB25LQxx, ZB25VQxx - otestováno na ZB25D40B.
23.4.2024 Do nové verze 2.34 byla přidána detekce FlashROM pamětí Puya Semiconductor P25QxxH a Macronix MX25L3255E - netestováno.
27.6.2024 Do nové verze 2.35 byla přidána detekce page EEPROM pamětí ST-Micro M95P08/16/32-I/E - netestováno.
FAQ:
Ohledně programu SPIPGM dostávám maily ze všech koutů světa a zdá se, že je to od doby uvedení asi můj nejstahovanější program. Děkuji za reporty testů pamětí, které jsem dosud neměl možnost sám otestovat. Některé dotazy a problémy se často opakují, proto bych je zde vypíchnul.19.3.2011 jsem se pustil do výroby nového hardware pro SPIPGM, který mi umožní snadno programovat i SMD paměti v pouzdru SOIC-8. Mailem mi také došlo pár fotek vašich programátorů:
- Při spuštění SPIPGM jen problikne černé okno a nic se nestane.
Tento program, jakožto naprostá většina mých dalších programů, se ovládá přes rozhraní příkazového řádku. Takže si nejprve račte spustit příkazový řádek s právy administrátora, přejděte do adresáře, kam jste program rozbalili, tam jej spusťte a vypíše vám krátkou nápovědu. Nepředpokládal jsem, že takové banality sem budu muset psát, ale řada mailů mě k tomu donutila.- SPIPGM s připojenou pamětí vůbec nekomunikuje, načtené JEDEC ID je FFFFFFh.
Tento problém může mít celou řadu příčin. Nejčastěji se jedná o chybu v hardware programátoru, jako např. vynechaný blokovací kondenzátor napájení čipu (nebo umístěný příliš daleko od čipu), slabý nebo zarušený napájecí zdroj, příliš dlouhé vodiče, vynechané nebo nevhodné hodnoty odporů na datových linkách MISO, MOSI, SCK a CS# (pro každý typ LPT a druh vodičů je optimální jiná hodnota, chce to vyzkoušet), slabší budiče LPT (např. na některých noteboocích), atd. Pokud se nedaří, můžete také zkusit zpomalit časování SPI sběrnice pomocí parametru /d=delay, kde delay je číslo v µs o které se prodlouží pulsy SCK. Tento parametr musí být uveden jako poslední. Další vhodné opatření je zapojit datové linky paměti přes buffery (např. 74HC04, 74LS125, 74HC244) umístěné u čipu, které posílí signály z LPT portu a zároveň sníží napěťové úrovně z TLL na 3,3 V, což je pro paměť šetrnější. Také pozor na to, pokud používáte jiný port než LPT1 nebo nějakou přídavnou kartu, která má LPT port na jiné I/O adrese (v práci mám PCI-E adaptér, kde je na E800h), tak nezapomeňte na parametr /l=lpt_base_io_address, kde lpt_base_io_address je bázová I/O adresa zapsaná buď dekadicky nebo hexadecimálně s prefixem 0x.- SPIPGM s připojenou pamětí komunikuje špatně, načtené JEDEC ID není FFFFFFh, ale hlásí parity error nebo dojde k chybě později při porovnání naprogramovaných dat.
Zde platí totéž, co bylo zmíněno výše. Pokud nepomáhá zpomalení timingu ani výměna odporů, doporučuji osadit buffer. Nejméně 2 lidem to už problém vyřešilo.- SPIPGM nemůže paměť odemknout.
Zamykací bity jsou u různých pamětí specifické podle výrobce, takže SPIPGM nemusí vždy umět paměť odemknout (zejména nové, dosud netestované typy). V takovém případě můžete zkusit natvrdo vynulovat status registr příkazem /s 0. Někteří výrobci, jako např. Winbond, už implementovali některé zamykací bity jako OTP (One Time Programming), čili jednou nastavené bity nelze vynulovat. V takovém případě nezbude, než koupit novou nenaprogramovanou paměť.- Lze naprogramovat paměť na základní desce přes SPI header?
Já jsem to zatím neměl možnost vyzkoušet, ale můj známý Franta Ryšánek takto uspěl při programování jedné desky od MSI, jeho popis si můžete přečíst zde. Desku měl odpojenou od PC zdroje a externě dodával pouze napájení 3,3 V pro paměť. Pokud není paměť rozpoznána, může pomoci dočasné zvýšení napájecího napětí až na 3,6 V a když se chytí, tak zas stáhnout na 3,3 V. V některých případech, pokud to specifikace paměti dovoluje, se zas osvědčilo napájecí napětí snížit na dolní mez.- SPIPGM bez problému paměť přečte, ale při programování nezapíše ani Byte.
V některých případech můžete narazit na paměť, která nepodporuje některé SPI příkazy. Zatím jsem na takovou nenarazil, takže nemůžu program jednoduše odladit. Vyzkoušejte program flashrom s parametrem -p rayer_spi, který podporuje více čipů.- Bude fungovat SPIPGM také v 64-bit Windows?
V současnosti nikoliv. Napřed bych musel přepsat a překompilovat svůj kernel mode driver, který umožní SPIPGM přistupovat na LPT port. Zatím s programováním pro 64-bit OS nemám žádné zkušenosti. Navíc je zde problém s podepisováním ovladačů, jako hobbista těžko dostanu od někoho certifikát. Proto použijte OS, který nehází programátorům klacky pod nohy, např. Live Linux CD nebo bootovací image FreeDOSu. UPDATE: 5.10.2018 jsem portoval svůj kernel mode driver na 64-bitová Windows, který ale nemám jak podepsat. Proto je nutné při startu Windows vypnout vynucení digitálních podpisů ovladačů.- Lze s SPIPGM naprogramovat 1,8V paměť?
Ano, z hlediska příkazů se paměti nijak neliší, akorát je potřeba paměť napájet stabilizovaným zdrojem 1,8 V a datové linky připojit přes převodník úrovní. Řada výrobců nabízí tyto obvody pod názvem "voltage level translator". Já jsem si postavil jednoduchý obvod z pár odporů, diod a tranzistorů, schéma zapojení zde.
SUDOKU.EXE ver. 1.2 [55 kB] (nepotřebuje CWSDPMI, má CWSDSTUB) - Když jsem poprvé luštil Sudoku, hned jsem si říkal, že je to úplně ideální úloha pro počítač. Algoritmus je jednoduchý, jen vyžaduje trochu více paměti než je schopen běžný člověk při hře použít (bez psaní čísílek na stranu papíru bych se nechytal). Tak jsem se do toho pustil a po pár hodinách byl Sudoku Solver 1.0 na světě. Podporuje hru v matici 9x9 o 9-ti blocích 3x3. Zadání lze naťukat z klávesnice nebo předat textový soubor jako parametr.
VESATEST.ZIP ver. 1.47 [166 kB] - pro obsluhu grafiky jsem si napsal vlastní VESA VBE driver, VESATEST otestuje kompatabilitu vašeho VESA BIOSu a mojeho driveru. Po proběhnutí testu se vytvoří log soubor, pošlete mi laskavě tyto výsledky.
24.3.2005 jsem zčásti předělal a vylepšil svůj VESA driver, nyní např. podporuje možnost nastavení režimu Write-Combining pro Linear FrameBuffer a BS okno pomocí MTRR registrů (procesory Pentium Pro a výš), čímž se až několikanásobně urychlí přenos dat z RAM off-screen bufferu do LFB. Toto nastavení příznivě ovlivní i jiné DOSovské programy (ovladače pro Windows obvykle nastaví W-C režim automaticky). Nastavení zůstává platné až do tvrdého resetu.
10.3.2011 Nová verze 1.45 má vylepšené funkce nastavení MTRR registrů tak, aby fungovaly i pod starým správcem paměti emm386.exe a Windows 9x. V režimu přepínání banků se nyní volá přímo přepínací funkce video BIOSu přes far pointer místo služby INT 10h, což by mělo být o něco rychlejší. Opravil jsem také pár menších chyb. Do balíčku jsem navíc přidal utilitku gtfcalc.exe včetně zdrojového kódu, která vygeneruje podle zadaného rozlišení a obnovovací frekvence monitoru soubor crtc.cfg s nastavením CRTC registrů dle specifikace VESA GTF. Pokud se tento soubor nachází v aktuálním adresáři spolu s VESATESTem, tak ten si z něj načte rozlišení, hodnoty CRTC registrů a další nastavení jimiž překryje výchozí nastavení nebo parametry z příkazové řádky. Dále pracuju na portaci své knihovny pod Windows s využitím knihovny SDL, tak abych mohl své grafické prográmky kompilovat beze změny zdrojového kódu jak v DJGPP pro DOS, tak v MinGW32 pro Windows.
11.11.2018 Nová verze 1.47 má vylepšené funkce nastavení MTRR registrů, detaily viz MTRRLFBE.
W83977D.EXE ver. 1.0 [49 kB] - Winbond superIO controller W83977EF registers controll utility slouží k výpisu a modifikaci registrů tohoto obvodu, který se hojně používal na základních deskách jako integrované periferie (obsahuje FDC, KBC, LPT, 2xUART, ACPI, 2xGPIO).
WINFALL.EXE [64 kB] - moje 1. 64kB intro v DJGPP, které se umístilo 8. z 10-ti na Fiasku 2000.
YMF7XX ver. 1.1 [55 kB] - jednoduchá utilita na zapnutí S/P-DIF výstupu na PCI zvukových kartách Yamaha YMF72x/73x/74x/75x v DOSu. Umožňuje také vypsat a nastavovat MMIO registry DSP, bez parametru vypíše nastavení dle PCI konfiguračních registrů.
ZALIGN ver. 1.2 [29 kB] - jednoduchá utilita na zarovnání souboru (např. ROM image) na celistvé násobky kB nulama nebo požadovanou výplní (hodnota Byte 0-255 dekadicky).
TIPY A TRIKY
Zde bych uvedl pár tipů na co si při psaní programů (nejen) v DJGPP/C dát pozor. Pokud se vám zdá, že se váš program chová poněkud nestandardně, či se právě chystáte v amoku prohodit počítač oknem, zkuste si přečíst pár následujících řádků. Na těchto případech jsem si sám osobně nabil hubu a jak se říká, chybama se člověk učí...
- Inicializace proměnných - v C vám nikdo nezaručí, že proměnné budou mít při startu programu nějakou definovanou hodnotu. I když je tam většinou nula, nelze se na to v žádném případě spoléhat. Zvlášť nebezpečné jsou neinicializované pointery - takto jsem se jednou spálil, když jsem na konci programu uvolňoval paměť funkcí free(...) odkazující se na neinicializovaný pointer (byl využíván jen v některých případech) a divil se, že to občas spadne. Inicializaci je možné provést spolu s deklarací proměnné, např. int i=0; nebo pro struktury a pole funkcí memset(...);.
- Přetečení proměnných/chybná indexace - C nijak nekontroluje přetečení proměnných nebo indexování mimo hranici pole (kvůli rychlosti). Proto je dobré se vždy zamyslet, jaké hodnoty může index pole nabývat a případně to nějak ošetřit. Při zápisu mimo pole můžou nastat zhruba tyto 3 případy:
1) přepíšete si pouze nějaké jiné proměnné - program funguje chybně
2) přepíšete nějakou oblast paměti, která nepatří vašemu programu - zde nejspíš zareaguje správce paměti tím, že program shodí a vypíše chybu o porušení ochrany paměti
3) přepíšete nějakou důležitou systémovou oblast - pokud to memmgr nevychytá tak reset/zatuhnutí PC.
Obzvláště zapeklité jsou případy, kdy dojde k přetečení nikoliv proměnné, ale vnitřní hodnoty ve výrazu (registru). Nedávno jsem to zrovna řešil při portování dekodéru JPEGu, který v 32-bit DJGPP/GCC fungoval bezchybně, ale v 16bit Borland C už ne (kompilátor s -Wall nehlásil žádné chyby a pak že C je vysoce portabilní jazyk). Podívejte se dobře na tento řádek:
QuantTable[temp0][xp][yp]=(JPGGetByte()*aanscales[(xp<<3)+yp])>>12;
QuantTable a aanscales jsou pole typu Word, funkce JPGGetByte() vrací typ Byte. V případě Borland C je vnitřní výraz JPGGetByte()*aanscales[(xp<<3)+yp] vyhodnocován v 16-bitovém registru a když tedy násobíme 16*8 bitů tak nám to samozřejmě přeteče, vyšší bity se ztratí a pak už nepomůže žádná rotace zpět. 32-bitový překladač DJGPP/GCC výraz vyhodnocuje ve 32-bitovém registru a tak k žádnému přetečení nedojde, výraz se rotuje 12 bitů doprava, takže se do Wordu vejde. Najít tuto chybu mi trvalo několik hodin, přitom náprava byla velice jednoduchá, stačilo uvést explicitní přetypování vnitřního výrazu na 32-bit DWord.
QuantTable[temp0][xp][yp]=((DWord)JPGGetByte()*aanscales[(xp<<3)+yp])>>12;
- Nekonstantní velikost standardních datových typů - z Pascalu jsem byl zvyklý, že když napíšu int tak mám prostě 16bit proměnnou, nic víc nic míň. Ne tak v C. Zdá se, že velikost je zcela na libovůli tvůrce překladače a obvykle se uzpůsobuje pro danou architekturu. Proto má DJGPP int 4-bajtový. GCC pro 64-bit procesory bude mít int zřejmě 8-bajtový. Ve většině případů nám to nemusí nijak vadit (kromě případné vyšší konzumace paměti u velkých polí), problém nastane, když máme pracovat s nějakou pevně definovanou datovou strukturou - např. při čtení hlavičky nějakého souboru do struktury si nemůžeme dovolit ponechat její velikost náhodě, protože formát souboru byl jednou stanoven tak a tak. Proto je dobré v takových strukturách nepoužívat standardní datové typy ale nadefinovat si vlastní, které můžeme kdykoli snadno předefinovat. Já jsem si takto definoval typy Byte, Word, DWord a QWord pro 8, 16, 32, 64 bitů do vlastního h-souboru, který includuju v každém programu.
- Memory alignment - to že jsme správně definovali velikosti datových typů nám ještě nezaručí, že struktura z nich složená bude mít správnou velikost. Kdysi jsem se dost divil, že mi sizeof(...) vrací velikost 4 B u struktury složené ze dvou bajtových položek, což by mělo být 2 B. Je to způsobeno úžasnou fíčurou zarovnání paměti (na sudé adresy, pro rychlejší přístup), která je u některých překladačů defaultně zapnutá. V GCC je třeba použít u položek struktury kouzelné slůvko __attribute__((packed)), které zarovnání paměti pro danou proměnnou potlačí. Naopak v případě používání instrukcí SSE je vyžadováno zarovnání proměnných na 16 Bytů, což lze zajistit pomocí __attribute__((aligned(16))).
Někdy je také potřeba alokovat blok paměti zarovnaný na násobky velikosti stránek (typicky 4096 B). K tomu slouží funkce valloc(...) (ze stdlib.h), která je proti malloc(...) specifická pro DJGPP.
- sizeof(...) a dynamické proměnné - pokud deklarujeme statické pole např. Byte a[10];, vrátí sizeof(a); hodnotu 10. Avšak pro dynamicky alokované pole Byte a=(Byte *)malloc(10); vrátí sizeof(a); hodnotu např. 4, odpovídající velikosti pointeru. Jak zjistit skutečnou velikost dynamicky alokovaného pole nevím. Zřejmě je potřeba si informaci o alokované velikosti uchovat v další proměnné.
- Správná volba režimu otevření souboru - soubory můžeme rozdělit na textové a binární. Jak je známo, tak textové soubory na systémech DOS a Windows používají jako oddělovače řádků dva bajty - znaky CR a LF, zatímco na Unixech pouze jediný bajt LF. Otevíráme-li tedy v DJGPP textový soubor s oddělovači řádků CRLF použijeme fopen(...,"r"); zatímco u binárních souborů použijeme fopen(...,"rb"); (rb jako read binary). To, že je slušností všechny otevřené soubory (i když otevřené jen pro čtení) taky pouzavírat funkcí fclose(...) (nejlépe hned poté, jakmile už z nich nebudeme dál číst) snad nemusím zdůrazňovat.
- Velikost písmen názvů souborů předávaných programu přes argv[] - case sensitivita je zlo. Zvlášť v operačních systémech, které negarantují konzistenci velikosti písmen v názvech souborů, jako jsou M$ Windows. V případě souborů, jejichž název splňuje DOSovské pravidlo 8.3, se totiž různé verze Windows chovají různě, alespoň na FAT. Pokud je pod Windows 9x vytvořen takový soubor velkými písmeny, uloží se v adresářovém záznamu velkými písmeny. Jestliže obsahuje alespoň jedno malé písmeno, je vytvořen LFN záznam v UNICODE, který Windows 9x i NT-based zpracovávají přednostně. Naproti tomu pokud je pod Windows NT vytvořen soubor s všemi malými písmeny, tak se místo vytvoření LFN záznamu zapíše flag do rezervovaného bajtu v adresářovém záznamu. S tím ale Windows 9x nepočítají a zobrazí soubor velkými písmeny. Navíc při kopírování takových souborů pod Windows NT systém tyto "nadbytečné" LFN záznamy maže. Při přenosu souborů z Windows NT do Windows 9x tedy může dojít ke změně velikosti písmen v názvech souborů, opačně nikoliv.
Start-up kód DJGPP programu provádí také filtrování velikosti písmen a expanzi (globbing) wildcards (*, ? znaků). Implicitně jsou všechny názvy souborů 8.3 konvertovány na malá písmena. V některých případech to může vadit, což lze změnit nastavením proměnné prostředí FNCASE=Y nebo přímo v programu nastavením flagu _CRT0_FLAG_PRESERVE_FILENAME_CASE v proměnné _crt0_startup_flags, viz následující kousek kódu:
#ifdef __DJGPP__ #include <crt0.h> int _crt0_startup_flags = _CRT0_FLAG_PRESERVE_FILENAME_CASE; #endif
- Válka indiánů - pro uložení vícebajtových čísel existují dva (doufám že ne více ;-) používané principy: Motorolácký Big endian, kdy se číslo v paměti ukládá od nulté adresy postupně od nejvíce významného bajtu po nejméně významný bajt a Intelácký little endian, kdy se nejprve ukládají nejméně významné bajty. Např. číslo FA821A57 se podle Big endianu uloží jako FA, 82, 1A, 57 zatímco podle little endianu jako 57, 1A, 82, FA (adresy rostou zleva doprava). Mě osobně je sympatičtější Motorolí Big endian. Uvádím to zde proto, že i na PC je potřeba zpracovávat soubory z jiných platforem a např. i ve výše zmiňovaném formátu JPEG se používá zrovna Big endian.
- Granularita funkce delay() - jak jsme byli zvyklí, tak slušně vychovaná funkce delay(...) akceptuje jako argument čas v milisekundách s rozlišením na 1 ms. Při běhu v NTVDM pod Windows NT/2k/XP ale systém delay ignoruje kvůli snížení zátěže CPU. V DJGPP 2.04 byla funkce delay(...) přepsána tak, aby chodila i v NTVDM, avšak granularita se zvýšila na 18,2 ms (standardní rozlišení časovače), což může v některých případech způsobit problémy. Zjistil jsem, že v NTVDM funguje korektně funkce uclock(...), která vrací počet tiků vysokorychlostního časovače (< 1us) a pomocí ní si napsal vlastní funkci delay, která zdá se funguje bez problémů. Jen pro informaci, v MinGW32 je přímý ekvivalent windowsí funkce Sleep(...), též s rozlišením 1 ms.
- Runtime kontrola porušení zásobníku - občas se při ladění záhadně padajícího programu může hodit detekce, že někde došlo k porušení obsahu zásobníku, např. přepsání lokálních proměnných ve funkci mimo rámec. V takové situaci nemusí dojít k běžné chybě ochrany paměti a program zhavaruje později nebo "jen" funguje chybně. Od verze GCC 4.1 přibyla funkce GCC Stack-Smashing Protector (ProPolice). Ta funguje na principu pokusného kanárka. Překladač změní prolog a epilog kód funkcí tak, že na začátku volané funkce se do zásobníku vloží speciální blok dat - tzv. kanárek, který se na konci před návratem z funkce zkontroluje. Pokud se změnil, je kanárek mrtev :). K tomu typicky dojde při překročení indexu pole nějakého bufferu během sekvenčního zápisu. Jestliže však index pole ustřelí o kus dál, nemusí to tento způsob detekce odhalit. Ale pořád lepší než drátem do oka... Zde je jednoduchý příklad pokus.c na otestování SSP:
int pokus(int x) { char buffer[10]; int moo=0x55AA; buffer[10]='J'; return(x+moo); } int main() { pokus(1); return(0); }
Překlad se zapnutým SSP se provede pomocí těchto parametrů: gcc -Wall -fstack-protector-all -lssp -o pokus.exe pokus.c. Po spuštění by se měla objevit hláška: *** stack smashing detected ***: terminated. Bez SSP program normálně skončí bez chyby.
- Agresivní optimalizace GCC - s tím, jak se GCC neustále vyvíjí a zlepšují se jeho optimalizační schopnosti, tak občas dochází i k optimalizacím kódu, které mají nežádoucí vedlejší efekty, jenž mohou způsobit chybné výsledky výpočtů nebo havárii programu. Někdy ani chyba nemusí být na první pohled zřejmá a projeví se až v nejméně vhodnou chvíli. Pokud program crashne, máte aspoň možnost najít a opravit problémovou funkci. Optimalizace GCC se zapíná přepínačem -Ox, kde x je typ / úroveň optimalizace [0..4]. Obvykle se zapisuje v Makefile projektu do proměnné CFLAGS. Nejčastěji se používá -O2 pro optimalizaci na rychlost nebo -Os pro optimalizaci na velikost kódu. Při -Os se používají některé optimalizace -O2, jenž nezvětšují velikost kódu. Optimalizace -O3 se používá už méně často, protože je u ní větší pravděpodobnost výskytu nežádoucích vedlejších efektů. Zapeklitost problému tkví v tom, že každá verze GCC může dělat při dané optimalizaci něco jiného. Občas se tak stává, že program kompilovaný starší verzí GCC, který fungoval korektně, začne při kompilaci novější verzí GCC záhadně padat (při stejné úrovni optimalizace). Nikdy si nemůžete být jisti, že až znovu přeložíte váš program novější verzí GCC, tak bude fungovat správně. Pokud na takové chování narazíte a víte že program dřív fungoval, zachovejte paniku a zkuste to znovu přeložit s -O0 nebo -Os. Pokud program bude fungovat, pokuste se najít problémovou funkci. Možná v ní někde chybí třeba kouzelné slovíčko volatile nebo je nekompletní clobberlist u inline assembleru. Pokud na žádnou chybu ve zdrojáku nepřijdete, je nejjednodušší cesta zbavit se problému selektivním vypnutím optimalizace pro celou funkci pomocí atributu. Tuto možnost nabízí GCC 4.4.0 a vyšší. Zde je ukázka definice takové funkce s potlačenou optimalizací na -O0:
static int __attribute__((optimize(0))) exec_ring0_code(DWord code_address, DWord ecx, DWord *eax, DWord *edx)
Je také možné optimalizaci vypnout pro jeden zdroják nebo jeho část pomocí pragmy:
#pragma GCC push_options #pragma GCC optimize ("O0") blok kódu #pragma GCC pop_options
Pokud vynecháme push_options a pop_options, bude vynucená optimalizace platit až do konce zdrojového souboru.
- Přetečení max. počtu relokací v jednom object souboru - při překladu nějakého složitějšího projektu jako např. FFMPEG se vám může stát, že překlad nějakého modulu skončí s podobnou chybou:
CC dsputil.o e:/djgpp/bin/as.exe: dsputil.o: /68: reloc overflow: 0x15b32 > 0xffff L:\WINXP\TEMP/ccOqvtFj.s: Assembler messages: L:\WINXP\TEMP/ccOqvtFj.s: Fatal error: can't close dsputil.o: File truncated make.exe: *** [dsputil.o] Error 1
Jak jsem zjistil, tak formát COFF (Common Object File Format) má limit 65536 relokací v jednom souboru. Pokud je tedy zdrojový soubor příliš velký, vytvoří se při kompilaci tolik relokací, že je nelze všechny uložit. Řešení je několik: je možno zdrojový soubor rozdělit na 2 menší nebo použít selektivně menší úroveň optimalizace (viz tip výše), která obvykle vygeneruje méně relokací. To jsou ale dost nesystémová řešení. Po té, co jsem na tento problém upozornil 25.12.2011 na DJGPP Google group se rozjela celkem zajímavá diskuze, kde bylo zmíněno (již existující) řešení, jak rozšířit COFF formát a nakonec 7.1.2012 vypustil Juan Manuel Guerrero novou verzi BinUtils 2.23.2, která rozšiřuje počet relokací na teoretických 4294967296 v jednom souboru. Bohužel však této hodnoty nepůjde tak snadno dosáhnout, protože cc1.exe může dojít paměť na zásobníku. Každopádně to dovolí překročit původní limit alespoň několikanásobně. Jak jsem se v praxi přesvědčil, FFMPEG se nyní zkompiloval bez problémů, download zde.
- Omezená délka příkazového řádku - při kompilaci velkého projektu FFMPEG 2.0 jsem narazil na problém, že Makefile skript volal AR s hromadou objectfajlů tak, že došlo k překročení maximální délky příkazového řádku a tím k chybě při vytváření knihovny libavcodec.a. Odchytil jsem si expandovaný příkaz do souboru pomocí make V=1 >log.txt a zjistil, že celá řádka má délku 14379 znaků. Různé operační systémy mají různá omezení délky příkazového řádku: v DOSu je to pouhých 126 znaků, ve Windows NT 4.0 a 2000 je to 2047 znaků, ve Windows XP je to 8191 znaků a v Linuxu typicky přes 100000 znaků (lze zjistit příkazem getconf ARG_MAX). DJGPP však toto omezení obchází vlastním způsobem, kdy si nativní DJGPP programy argumenty předávají pomocí transfer bufferu v dolní paměti místo volání shellu. Výchozí velikost transfer bufferu je 16 kB a využívá se ještě pro proměnné prostředí a při volání realmode funkcí DOSu/BIOSu. Pomocí programu stubedit.exe program.exe bufsize=xxk lze velikost transfer bufferu zvětšit až na 64 kB. Já jsem nastavil velikost 32 kB pro make.exe, ar.exe a rm.exe, kde docházelo k překročení limitu. Aktuální nastavení STUBu binárky lze zjistit příkazem stubedit.exe -v program.exe
E:\DJGPP\BIN>STUBEDIT.EXE -v AR.EXE -value- -field description- 0x80000 (512k) Minimum amount of stack space (bytes/K/M) 0x8000 (32k) Size of real-memory transfer buffer (bytes/K/M) "" Base name of file to actually run (max 8 chars, ""=self) "" Value to pass as file component of argv[0] (max 16 chars, ""=default) CWSDPMI.EXE Program to load to provide DPMI services (if needed)
- Přesměrování adresáře %WINDIR%\SYSTEM32\DRIVERS na 64-bitových Windows - doposud jsem žádné 64-bitové Windows nepoužíval a tak mě toto chování systému během portování mého kernel ovladače z 32 na 64-bitů kapánek překvapilo. Když jsem chtěl pomocí své 32-bitové DLLky vyextrahovat 64-bitový ovladač do adresáře %WINDIR%\SYSTEM32\DRIVERS, tak se soubor bez chyby vytvořil, ale nemohl jsem ho na daném místě najít. Ještě větší WTF moment jsem zažil, když pomocí příkazu ls (z 32-bitového MinGW32) jsem soubor viděl na správném místě, ale příkazem dir nikoliv. Až když mě napadlo soubor vyhledat (64-bitovým Total Commanderem), tak jsem zjistil, že se nachází v adresáři %WINDIR%\SysWOW64\DRIVERS, ha a jsme doma. 64-bitové Windows totiž tímto transparentním přesměrováním celkem logicky udržují pořádek, aby se nemíchaly 32 a 64-bitové ovladače a knihovny. Jak ale zajistit, aby mohl i 32-bitový program přistupovat k 64-bitovému systémovému adresáři? Existuje na to substituce sysnative místo system32. Tu však nelze použít např. v registrech v záznamu služby (položka ImagePath), takže jsem musel napsat funkci, která cestu dynamicky upravuje podle toho, na jaké verzi Windows běží.
- = MINGW32 = -
Tak, a teď něco pro 'vývojáře'. Ne, nebojte se, zas tak hrozný to nebude. Jen tak mimochodem, je zajímavé, jaxe lidi, co vytvářejí programy rozdělili na 'vývojáře', 'programátory' a 'codery'. Já se tedy považuji nejspíše za klasického programátora. Akorát mě ještě zcela nepohltila iluze, že kilo salámu váží 1024g. :-) Takové problémy nemusí vývojáře vůbec zajímat, neboť ti jen natahají do projektu ikonky a přiřadí jim procedůrky. Ti by si měli napřed přečíst, jak to fungovalo dříve a kam jsme to dopracovali. Proč tedy o tom melu: i programátor někdy potřebuje něco naprogramovat pro Windows. A proto je tu MinGW pro normální programátory, které jim umožní vytvářet W32 appz klasickým způsobem. MinGW je svým principemvelmi podobné DJGPP - tedy překladač a pár utilit, tzv. GNU toolchain. Pro srovnání jsem si nainstaloval M$ Visual C++ 6.0 a když jsem sehnal MinGW32, taxem VC s radostí smazal.
Abych nešířil paniku, že snad chci přeběhnout k Windozákům, tak jen uvedu, že jelikož se zajímám o grafiku, taxi taky nemohu nechat ujít programování v OpenGL, které je nyní velmi populární, ať už proto, že jsou konečně na trhu grafické karty za relativně lidovou cenu, které OpenGL kvalitně akcelerují, nebo proto, že OpenGL je velice univerzální - běží totiž na SGI, W32, Linuxu, FreeBSD, snad i BeOSu a dokonce i v DOSu! Bohužel jen softwarově (HW driver je zatím jen pro 3Dfx voodoo).
+ MinGW32 je GNU software, to znamená že si ho stáhnete zadarmo i se zdrojáky. + GCC je stále vyvíjený překladač a jeho aktuální verze je 8.1.0. + Podpora 64-bitových Windows a tvorba 64-bitových aplikací variantou MinGW64 (pro CPU 686+). + Možno používat linux-like vývojové prostředí MSYS2 s BASHem, poslední verze funkční pod Windows XP je 2.5.2. + Generuje poměrně malé binárky. + Na disku zabere jen asi 30 MB. - Vhodné spíše pro konzolové appz, což v případě SDL/OpenGL nevadí.
Aktuální verze MinGW32 je zde: http://www.mingw.org a zde jsou další Win32 porty různých GNU utilit a knihoven.
Zde je vůbec můj první windozácký program, který něco zobrazí v OpenGL okně: POKUS.EXE [3 kB]. Pro jeho spuštění je třeba standardní knihovna opengl32.dll, kterou jistě všichni máte ve svých Windows a dále GLUT32.DLL [68 kB] kterou už asi všichni nemáte...
5.9.2012 DŮLEŽITÉ UPOZORNĚNÍ: MinGW32/64 od verze GCC 4.7.0 a vyšší používá defaultně zapnutý parametr -mms-bitfields (dříve byl implicitní -mno-ms-bitfields). To se prý týká i GCC na 64-bitovém Linuxu. Tato vlastnost má velmi nepříjemný důsledek v tom, že datové struktury, které byly označeny __attribute__ ((packed)) nejsou nyní pakované na minimální velikost, protože Micro$oft asi chápe pakování jinak. To může velmi zásadně porušit kompatabilitu existujících knihoven a nového kódu. Jedna možnost je přidat do Makefile GCC parametr -mno-ms-bitfields, čímž se zajistí chování jako dříve (vhodné pro překlad starších knihoven). Ve vlastním kódu je také možno použít atribut gcc_struct (jeho protějšek je ms_struct, byly zavedeny už od GCC 3.4) ve spojení s atributem packed, tedy __attribute__ ((gcc_struct, packed)) a pak je zaručeno, že struktura bude vždy pakovaná. Teda možná jen do příští verze GCC :). Více informací zde, zde a zde.
25.10.2013 jsem po updatu balíčku w32api-4.0.3 z 22.9.2013 objevil chybu v knihovně libntoskrnl.a, která se odkazuje na neexistující soubor ntoskrnl.dll (testováno na Windows XP SP3) místo ntoskrnl.exe. Chybu jsem nahlásil, ale po měsíci se zatím neděje. Pro zatím jsem se vrátil ke starší verzi knihovny libntoskrnl.a z 1.4.2013, která funguje správně.
21.1.2015 Tím, že se MinGW snaží co nejvíce využít stávající WinAPI funkce a knihovny, vznikají občas nepříjemné potíže s kompatabilitou. Např. funkce printf(), která se implicitně volá z dynamické knihovny msvcrt.dll, nepoužívá standardní (ANSI C) specifikátor velikosti proměnné ve formátovacím řetězci pro dlouhý int long long: %ll ale %I64. Také nepodporuje typ long double, resp. v MSVC je shodný s double a doporučuje se tak při tisku jeho přetypování na double, protože implementace long double v GCC má jinou velikost. Jednou možností, jak to řešit, je definovat si konstantu s vlastním formátovacím řetězcem podle použité platformy a pak při tisku rozdělit formátovací řetězec na více částí, např.:
#ifdef __WIN32__ #define _MYPFI64 I64 #else #define _MYPFI64 ll #endif unsigned long long tsc=...; printf("TSC = %"_MYPFI64"Xh", tsc);
Další možností je použít alternativní funkci __mingw_printf(), případně s ní podmíněně předefinovat printf(). Toho je možné dosáhnout i bez zásahu do zdrojového kódu použitím přepínače -D__USE_MINGW_ANSI_STDIO při kompilaci (zakomponovat do Makefile). V takovém případě se použije staticky linkovaný modul s funkcí printf(), který výsledný kód po UPX kompresi zvětší asi o 10 kB. Zdá se, že Micro$oft tento standard nakonec akceptoval, v dokumentaci MSVC 2013 už specifikátor velikosti %ll existuje taktéž.
10.2.2019 Náhodou jsem narazil na problém kompatability binárek kompilovaných v novějším MinGW64. Když jsem se takový program pokusil spustit na starším laptopu s CPU AMD K6-2, tak crashnul, zatím co na trochu novějším laptopu s Pentiem III fungoval korektně. Jak jsem si všiml, MinGW64 pro Windows je kompilováno pro CPU rodiny 686, avšak AMD K6 nepodporuje hojně používanou instrukci CMOV. Zkusil jsem program překompilovat s parametry -march=i586 a -mtune=i586, ale padalo to stejně. Problém je, že instrukcí CMOV je zaplevelená už systémová knihovna libgcc.a a bez ní jsme v řiti. Překompilovat celé MinGW64 pro i586 se zřejmě nikomu nechtělo a tak jsem si musel na disku nechat i starší MinGW32 GCC 6.3.0 vedle nového MinGW64 pro kompilaci na všechny zájmové platformy.
- = SPHINX C-- = -
Jak již název napovídá, jedná se o mrňavou distribuci C překladače, který je speciálně určen na tvorbu malých programů *.COM a rezidentů. Takový helloworld.com pak zabírá ~70 B ! Optimalizuje pro CPU 8086-80586. Bohužel syntaxe není zrovna moc ANSI, takže se napřed budete muset seznámit s dodávanými příklady.
Zde je verze 1.04a C--.ZIP [979 kB], domovská stránka je tady: http://c--sphinx.narod.ru
- = Tiny C Compiler = -
Další mini distribuce C překladače pro 32 a 64-bitová Windows a Linux. Tentokrát však plně ANSI ISO C99 kompatabilní, navíc volitelně podporuje i kontrolu překročení hranic polí a některá rozšíření GCC. Avšak pozor, ne vše je dotažené na 100%, např. __attribute__((packed)) je nutné psát za každý člen struktury zvlášť, nestačí jednou na konci pro celou strukturu. Umí též inline assembler a překlad kódu jako DLL knihovny (i využívat jiných DLL). Dokonce je možné ve vlastním programu pomocí libtcc.dll kompilovat další zdrojový kód - JIT (Just-In-Time compilation). TCC se chlubí rychlostí překladu, že je až 9x rychlejší než GCC. Produkované EXE soubory jsou poměrně malé - jednoduchý konzolový program na výpočet Fibonacciho posloupnosti se přeloží na 2 kB.
Domovská stránka je zde: http://bellard.org/tcc
- = NASM = -
Tak a teď něco pro opravdu lowlevel programátory. Jelikož jsem se teď začal zajímat o modifikaci BIOSu, potřeboval jsem vytvořit kousek čistého binárního kódu. A v tom mě TASM zklamal. Naštěstí ale právě pro tyto účely existuje tento GNU překladač assembleru. Má však jiné direktivy a pseudoinstrukce, nicméně pro začátek nemusíte použít žádnou. Stačí jen posloupnost instrukcí. Dokonce ani na konci souboru nemusí být END. Taky se trochu liší syntaxe přístupu k paměti: MOV AL,[CS:SI+10] proti MOV AL,CS:[SI+10] a jiné drobnosti.
Nakonec z toho všeho vznikl projekt ROMOS, který umožňuje bootovat FreeDOS z ROMky. Na něm jsem se v assembleru docela vyřádil, takže rači zpět k pohodlí Céčka :). Pokud se chcete inspirovat, jak píší opravdoví mistři assembleru, tak doporučuju prostudovat zdrojáky z programátorské soutěže Hugi Size Coding Competition. Pokud jste naopak začátečníci, můžu doporučit knihu kolegy Rudolfa Marka - Učíme se programovat v jazyce Assembler pro PC, kde jsou popsané naprosté základy od binární a hexadecimální soustavy, přes architekturu PC a popis instrukcí po praktické ukázky použití systémových volání OS DOS, Windows a Linux.
Zde je ke stažení NASM ver. 0.98.38 [116 kB], domovská stránka s aktuální verzí je tady: http://nasm.sourceforge.net
- = MCU-8bit: WinAVR = -
Poprvé jsem se dostal k mikropočítačům někdy v roce 1997 na SPŠE. Tam jsme se učili o legendárním 8-bitovém procesoru Zilog Z80. Na hardware jsme si ale bohužel ani nesáhli a veškerá praxe se odbývala jen v simulátoru EASYASZ80 1.30. A tak jsem si na univerzálním plošňáku postavil vlastní vývojový kit s eNDeRáckým klonem UA880D, paticí na RAM/EPROM a LEDkama na sběrnicích. Cyklus programování/mazání EPROM, při kterém jsem jednou málem vyhořel, když jsem zapomněl dlouho zapnuté horské slunce, byl příliš zdlouhavý a ubíjející a tak jsem na tom nikdy nic kloudnýho nenaprogramoval. I když to je hadr proti tomu, když jsem četl, jak v dřevních dobách Miroslav Němeček naprogramoval na první pokus funkční monitorovací program (BIOS) pro svůj počítač do 1kB PROM.
Později jsem se setkal s mikropočítači řady 51 od Atmelu - malý ale šikovný (a levný) AT89C2051, který už měl na čipu 2 kB FlashROM. Programoval jsem ho pomocí paralelního programátoru PATMEL. Stále však bylo nutné vyjímat čip z obvodu a přendavat do programátoru, nicméně díky FlashROM se programovací cyklus mnohanásobně zrychlil. Na FELu jsem pak také dělal na jednom předmětu u Rozehnala (dokud tam ještě učil) s Motorolou MC68HC11, která už podporovala nahrávání programu přímo v cílovém obvodu - ISP (In System Programming) po sériové lince pomocí bootloaderu. Tento MCU byl ale hůře dostupný a na hraní poměrně drahý.
Vzhledem k malé velikosti paměti se tyto jednočipy programovaly výhradně v assembleru. 51-čkový assembler se dal celkem rychle naučit. Ten motorolí měl jistá specifika, která mi zrovna moc nesedla. Assembler umožňuje programátorovi absolutní kontrolu nad celým hardwarem a maximálně efektivně využít jeho vlastností. Kód je plně deterministický a lze přesně určit počet strojových cyklů na jeho provedení. Horší je to však s orientací ve složitějším projektu, nutnost vlastní zprávy paměti a přenositelností na jiné platformy (ASM instrukce odpovídá strojovému kódu instrukce konkrétního typu/rodiny procesorů).
Další významnou změnou pro mě bylo, když jsem se ve čtvrťáku setkal poprvé s MCU ATmega32 s 32 kB FlashROM, 2 kB SRAM a 1 kB EEPROM na čipu z rodiny Atmel AVR a programovacím jazykem C pro AVR. Céčko jsem se v té době učil na x86-PC jako samouk a objevoval jeho výhody. Možnost používat takto výkonný jazyk na MCU mi přišla naprosto úžasná. C umožňuje jednoduše vytvářet složitější konstrukce a řeší za programátora správu paměti, přitom lze snadno a efektivně provádět low-level operace na úrovni HW a vždy je tu možnost část kódu nainlajnovat v assembleru. AVR assembler se ale od x51 assembleru dost liší a já jsem pak nějak neměl potřebu se ho učit, když už jsem psal v C. Nicméně je dobré ho umět alespoň číst pro prohlížení toho, co vypadne z C kompilátoru a pro ladění. A uznávám, že dobrý programátor napíše program v assembleru pro MCU vždy lépe, než to udělá překladač jakéhokoliv vyššího jazyka, jenže takových lidí je dnes už dost málo. Z té mladší generace znám jen jednoho, Standu Mašlána, který to s AVRASM opravdu umí, respect.
Céčko otevírá na MCU dosud netušené možnosti díky přenositelnosti zdrojového kódu. Konkrétně pro AVR existují nejméně 3 různé překladače C: komerční CodeVisionAVR, IAR Embedded Workbench for AVR a OpenSource AVR-GCC, které je k dispozici pro Win32 (novější verze je zde a zde). a pro Linux (např. pro Debian Linux jsou k dispozici i binárky v .deb balíčku, novější verze pro Arch Linux je zde). Právě GCC, které najdete snad všude, umožňuje téměř dokonalou kompatabilitu s velkými stroji. Pro AVR je vyvíjena také osekaná verze LibC knihovny - AVRLibC. Díky tomu jsem např. mohl velkou část svojí diplomky (souborový systém FAT a částečně ATA driver) napsat a odladit na PC a následně s minimálními úpravami přenést na MCU. Co se týče rychlosti kódu, je na tom GCC znatelně lépe jak CodeVision a o trochu hůře jak IAR. Pro představu, kolik jaká operace či funkce v C zabere taktů, jsem udělal tento test (měřeno pomocí vnitřního 16-bitového časovače).
AVRko se dá jednoduše programovat přímo v daném obvodu buď přes JTAG nebo sériovou SPI sběrnici. SPI programátor typu BSD je velmi jednoduchý na výrobu, jde v podstatě jen o pár drátů vytažených z paralelního portu (jeden z důvodů, proč se mi nelíbí, že LPT mizí z notebooků a PC). Existuje zde však nebezpečí, že při nějaké nehodě kromě MCU vyhoří i paralelní port PC, což se mi jednou díky mé neopatrnosti opravdu stalo a tak je asi bezpečnější použít nějaký oddělovací obvod pro datové linky osazený v patici. Při použití plochého kabelu je vhodné dát mezi signály SPI země proti přeslechům. O naprogramování vnitřní FlashROM, EEPROM a fuse-bitů se postará program AVRDude, který lze volat přímo z Makefile (přidal jsem si to jako příkaz make flash). Je pro něj i grafická nadstavba AVRDude-GUI. Pozor na to, že novější build AVRDude v Arduino IDE používá novější a nekompatabilní verzi knihovny libusbK místo libusb-win32 a také od verze 6.4 byla vypuštěna podpora SPI programátoru typu BSD.
Novější malé MCU ATtiny4/5/9/10 (v opravdu mrňavém pouzdru SOT-23 či UDFN-8) používají programovací rozhraní TPI (Tiny Programming Interface) - piny TPIDATA, TPICLK a RESET#. K jejich programování lze použít novější programátor Atmel-ICE (je třeba AVRDude verze 7.1 a novější, ten už neběží na Windows XP) nebo low-cost programátor USBasp založený na MCU ATmega8/48/88 využívající knihovnu V-USB s poslední verzí firmware. Pozor na to, že programovaný MCU musí být napájený napětím 5 V (vnitřní nábojová pumpa by nezvládla vyrobit dostatečné programovací napětí při nižším napájení).
Pokud toužíte po IDE, tak firma Atmel nabízí zdarma ke stažení AVR Studio 4.19, které spolupracuje s WinAVR (není jeho součástí). Obsahuje editor, správce projektu a ladicí nástroje. Ty lze využít jen při propojení přes JTAG rozhraní (např. v práci používám JTAGICE mkII na USB). To přináší možnosti ladění jako na velkém PC - krokování v C i ASM, breakpointy, prohlížení stavu paměti a všech registrů. Ve stop stavu lze například na všech portech jednoduše naklikat stav jednotlivých I/O linek (DDRx, PORTx) a sledovat aktuální stav v případě vstupů. To se hodí při oživování nových desek, když si chci změřit, jestli se signál z portů dostává tam, kam má. Rozhraní JTAG však mají pouze některá AVRka (silnější ATmegy). Do těch menších bylo později přidáno rozhraní debugWIRE, které se aktivuje fuse-bitem DWEN. Tím se ale zruší funkce pinu RESET# a nelze programovat přes SPI, ani nelze přeprogramovat fuse bity. Pro deaktivaci debugWIRE je potřeba poslat speciální resetovací sekvenci, která aktivuje dočasně SPI a pak je možné fuse-bit DWEN vypnout. Pro ladění lze také s výhodou použít různé vývojové kity od Atmelu, z kterých lze drobnou HW úpravou udělat univerzální programátor a debugger.
Když jsem nedávno potřeboval znovu nainstalovat AVR Studio 4.19 pod Windows XP, tak se mi po instalaci odmítlo spustit a vyblaflo nějakou nespecifickou chybu MFC aplikace. Po několika hodinách bádání jsem zjistil, že problém způsobila mrkvo$oftí aktualizace knihovny oleaut32.dll z první poloviny roku 2018. Když jsem tuto knihovnu (verze 5.1.2600.7494, velikost 546816 B) nahradil starší verzí z roku 2017 (která byla asi o 17 kB větší), tak se AVR Studio spustilo bez problémů. Mezitím už Micro$oft problém vyřešil v další aktualizaci KB4466388 (ENG) z 12.11.2018, která obsahuje funkční knihovnu oleaut32.dll (verze 5.1.2600.7594, velikost 563200 B). Na další problém jsem narazil při instalaci AVR Studia 4.19 (resp. jeho USB ovladačů pro JTAGICE mkII) pod Windows 10 x64. Původní USB ovladače sice byly i 64-bitové, ale patrně jen pro Windows XP x64. Když jsem stáhl a nainstaloval novou verzi ovladačů driver-atmel-bundle-7.0.888.exe tak sice ve správci zařízení bylo vše v pořádku, ale AVR Studio se nemohlo k programátoru připojit. Zjistil jsem, že nové USB ovladače používají nový nekompatabilní komunikační protokol s kterým si rozumí jen novější Atmel Studia 6.2.x a 7.x z nichž se stal nechutný bloatware. Je tedy potřeba downgradovat soubor ovladače windrvr6.sys z verze 11.50 na verzi 11.30, která jako poslední s AVR Studiem ještě funguje. Zde je samotný windrvr6.sys pro 32-bitové a 64-bitové Windows. Nebo je možné z instalačního souboru Atmel Studia 6.1.2730 pomocí parametru /extract_all:unpacked vybalit instalační balíček s ovladači AtmelUSB.exe a klasicky nainstalovat. Pak jsem narazil na další problém s AVR-GCC toolchainem (konkrétně zlobil make při spouštění další procesů) pod Windows 10 x64, který se mi podařilo vyřešit aktualizací knihovny msys-1.0.dll (na verzi 1.0.18 nebo novější).
AVRka jsem si velice oblíbil. Jsou poměrně rychlá (standardně 8 nebo 16 MHz, většina instrukcí trvá 1 takt), dobře vybavené (různé on-chip periferie, výkonové výstupy [half-bridge] schopné přímo budit LED), blbovzdorné (už se mi povedlo párkrát přepólovat napájení nebo hustit proud do výstupního portu bez následků), dostupné (GME, GES) a levné (leckdy se vyplatí dát 30 peněz za 1 MCU, než se matlat s kupou standardních TTL/CMOS obvodů). Existuje celá škála od mrňousů ATtiny s 8 vývody a 1 kB FlashROM po velké ATmegy256x s 256 kB FlashROM, 8 kB SRAM a 4 kB EEPROM na čipu. Další verze mají nějakou speciální periferii nebo jsou určeny pro automobilový průmysl či pro bateriové aplikace se sníženou spotřebou a napájecím napětím - řada picoPower AVR. Doma i v práci jsem navrhoval zapojení či programoval celou řadu AVR (ATTiny2313, ATmega8, 1650, 32, 325p, 3250, 64, 6450, 128 a 2560, přes SPI i JTAG) a jsem s nimi velice spokojen. Takto vypadá moje bastldeska s ATmega32:
Pokud chcete začít programovat AVRka, odkážu vás na tutoriály pro začátečníky zde a zde. Nemusím snad zdůrazňovat, že základním zdrojem informací jsou datasheety a aplikační nóty firmy Atmel, kde je vše podrobně a vcelku srozumitelně popsané. Předpokladem je však znalost angličtiny a odborných termínů. Ti pokročilejší pak mohou hledat pomoc se zapeklitými problémy na fóru AVRFreaks. Bohužel v poslední době díky krizi je dostupnost AVRek bídná a ceny vysoké (i několikanásobně než dříve). Podle mých posledních informací už jede výroba zase naplno, ale téměř všechny součástky jdou velkým zákazníkům do výroby a nepatrný zbytek do distribuce. Snad se příští rok 2011 situace zlepší, nerad bych z nouze sahal po něčom jako PIC. Pokud vám výkon 8-bitového AVR už nestačí, můžete zkusit zaexperimentovat s 32-bitovým ARM7 LPC2103FBD48 od NXP, který nabídne za stejné peníze mnohem větší výkon.
29.8.2012 Každý programátor dělá chyby, ale nejhorší je, když musí lovit chyby v cizím kódu nebo se nemůže spolehnout ani na vývojové nástroje. Následující příběh se týká AVR-GCC 4.7.0 vydaného 24.4.2012. V práci jsem dostal za úkol ke své senzorové aplikaci na AVR ATmega88pa napsat ještě bootloader, aby šlo firmware aktualizovat přímo po vyvedeném RS232. Jako protokol pro přenos dat jsem se rozhodl implementovat XModem-CRC download, protože je to protokol jednoduchý a podporuje ho každý lepší terminálový program (už od dob DOSu). Dílo se celkem dařilo, ale protože jsem musel dost šetřit pamětí (ATmega88pa má boot sekci velkou max. 2 kB), hledal jsem další optimalizace. Jednou z nich, kterou už používám celkem automaticky je vytvoření samostatných sekcí pro funkce tak, aby linker mohl ty nepoužívané vypustit (jedná se o parametry GCC -fdata-sections, -ffunction-sections a -Wl,--gc-sections).
Dále jsem přešel na GCC 4.7.0, které lépe optimalizuje na velikost a ušetřilo mi asi 150 B kódu, ale tím jsem si také zadělal na problémy. XModem přenos náhle začal tuhnout. Né hned, ale až po té, co jsem zrušil dočasné globální proměnné. Funkce pro příjem XModem paketu strká data do pole, které je zde definováno jako static pole Bytů (ušetří taky trochu místa): static Byte packet_buffer[XMODEM_DATA_SIZE];. Jak jsem zjistil, tak po zápisu asi 30 Byte do bufferu se program zbláznil a nakonec zasekl. Bylo evidentní, že se zapisuje někam, kam se nemá, ale přitom nešlo o překročení hranice pole. Pokud jsem dal třídu static pryč, tj. buffer se alokoval se zavoláním funkce na zásobníku, tak problém zmizel. Pokud jsem vyhodil z Makefile parametry na vytváření samostatných sekcí, tak taky OK. Když jsem zakomentoval dočasně část kódu a přeložil starým GCC 4.3.3, tak vše fungovalo bezchybně i se static.
Po bádání s kolegou nad LST a MAP soubory, jsme objevili nemilé překvapení, kdy GCC 4.7.0 ze záhadného důvodu umístilo buffer v sekci .bss od adresy 0x60, kde se nachází různé systémové registry procesoru, místo od adresy 0x100, kde začíná SRAM. Takže docházelo při příjmu dat k přepisu těchto registrů, což celkem logicky vyústilo v havárii, dokonce mi i blikla LEDka (jelikož při ladění této funkce nemůžu používat UART pro ladicí výstupy a MCU má jen jeden, tak jsem si prostě blikal a chytal to na digitálním osciloskopu) Tato vlastnost se přitom projevovala pouze tehdy, pokud zde nebyly žádné globální proměnné a sekce .data tak byla prázdná. Linker jakoby sekci .data vypustil a posunul kvůli tomu začátek .bss. Náprava byla tedy už celkem jednoduchá, jednu proměnnou z funkce main() jsem vyšoupnul ven jako globální a vše zas fungovalo. Když jsem chtěl chybu reportovat na AVRFreaks, koukal jsem, že už vydali ke stažení RC verzi GCC 4.7.1, tak jsem ji samozřejmě vyzkoušel a hle, chyba byla opravena. Resp. linker script WINAVR\AVR\LIB\LDSCRIPTS\avr4.x, který stačí překopírovat do stávající verze AVR-GCC. Zde je ukázka z MAP souborů:
.data 0x00800100 0x0 load address 0x00001fb4 0x00800100 PROVIDE (__data_start, .) *(.data) *(.data*) *(.rodata) *(.rodata*) *(.gnu.linkonce.d*) 0x00800100 . = ALIGN (0x2) 0x00800100 _edata = . 0x00800100 PROVIDE (__data_end, .) .bss 0x00800060 0x82 0x00800060 PROVIDE (__bss_start, .) *(.bss) *(.bss*) .bss.start_main 0x00800060 0x2 bootldr.o 0x00800060 start_main .bss.packet_buffer.1868 0x00800062 0x80 bootldr.o *(COMMON) 0x008000e2 PROVIDE (__bss_end, .)špatně
.data 0x00800100 0x2 load address 0x00001fdc 0x00800100 PROVIDE (__data_start, .) *(.data) *(.data*) .data.timeout 0x00800100 0x2 bootldr.o 0x00800100 timeout *(.rodata) *(.rodata*) *(.gnu.linkonce.d*) 0x00800102 . = ALIGN (0x2) 0x00800102 _edata = . 0x00800102 PROVIDE (__data_end, .) .bss 0x00800102 0x82 0x00800102 PROVIDE (__bss_start, .) *(.bss) *(.bss*) .bss.start_main 0x00800102 0x2 bootldr.o 0x00800102 start_main .bss.packet_buffer.1876 0x00800104 0x80 bootldr.o *(COMMON) 0x00800184 PROVIDE (__bss_end, .)správně
15.1.2016 firmu Atmel koupil její konkurent Microchip. MCU AVR tím ale nekončí, měly by se vyrábět ještě alespoň 10 let a jsou připravované i nové typy (např. ATmegy s programovatelnou logikou). Aktuální přehled rodiny AVR je zde. A i kdyby náhodou, tak Číňani to jistí. Firma LogicGreen Technologies nabízí levné kompatabilní 8-bitové MCU, např. LGT8F88A. Ten má vyšší max. hodinovou frekvenci a některé instrukce trvají méně taktů. Zdá se, že Microchip 8-bitovou rodinu AVR (zejména ATtiny) nadále rozvíjí a přináší řadu novinek s nimiž vás ve svých tutoriálech seznámí Michal Dudka.
Microchip také převzal vývoj Atmel Studia, které integruje AVR-GCC toolchain a stalo se preferovanou vývojovou platformou. Naproti tomu samostatný balík AVR toolchainu stagnuje, neobsahuje aktuální verzi GCC, ani hlavičkové soubory pro nové čipy. Vývoj samostatné AVRLibC se už také zastavil. Je však stále možné do stávajícího toolchainu přidat podporu nových čipů. K tomu je potřeba stáhnout balíček Atmel.ATmega_DFP.x.y.z.atpack nebo Atmel.ATtiny_DFP.x.y.z.atpack, který přejmenujeme s koncovkou .zip a někam rozbalíme. Pro konkrétní MCU, např. novou ATmegu328PB najdeme a zkopírujeme potřebné soubory do našeho toolchainu: iom328pb.h -> WINAVR\AVR\INCLUDE\AVR, specs-atmega328pb -> WINAVR\LIB\GCC\AVR\8.0.1\device-specs, crtatmega328pb.o -> WINAVR\AVR\LIB\AVR5 a libatmega328pb.a -> WINAVR\AVR\LIB\AVR5. Pak je ještě třeba do souboru WINAVR\AVR\INCLUDE\AVR\io.h přidat 2 řádky:
#elif defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__) # include <avr/iom328p.h> #elif defined (__AVR_ATmega328PB__) # include <avr/iom328pb.h> #elif defined (__AVR_ATmega329__) || defined (__AVR_ATmega329A__) # include <avr/iom329.h>
a pokud programujete přes avrdude, bude potřeba také přidat pár řádků do avrdude.conf, protože nová ATmega má jinou signaturu a trochu jiné pojistky.
part parent "m328" id = "m328p"; desc = "ATmega328P"; signature = 0x1e 0x95 0x0F; ocdrev = 1; ; part parent "m328" id = "m328pb"; desc = "ATmega328PB"; signature = 0x1e 0x95 0x16; ocdrev = 1; memory "efuse" size = 1; min_write_delay = 4500; max_write_delay = 4500; read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0", "x x x x x x x x o o o o o o o o"; write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0", "x x x x x x x x x x x x i i i i"; ; ;
1.7.2016 Když jsem zas po čase aktualizoval svůj AVR-GCC toolchain, zjistil jsem, že některé mé starší zdrojové kódy nejdou přeložit, protože interní chyba překladače:
gpsnmea.c: In function 'gps_parse_string': gpsnmea.c:306:1: internal compiler error: in push_reload, at reload.c:1380 } ^ gpsnmea.c:306:1: internal compiler error: Segmentation fault make: *** [gpsnmea.o] Error 1
Zde konkrétně chybu způsobuje složitější příkaz switch() a pokud z něj vyházím skoro všechny větve case, tak chyba zmizí. Chybu se mi podařilo reprodukovat s AVR-GCC verze: 4.8.0, 4.9.2 (z poslední verze balíku Atmel Studio 7), 5.2.1, 6.1.1 a 6.3.1. Přitom se starší verzí 4.7.2 vše funguje v pořádku. Na fóru AVRFreaks jsem našel více vláken (i několik let starých) zmiňujících tuto chybu. Dokonce už měla být dle Bugzilly opravená, ale zřejmě ne pro všechny případy. Jako workaroud lze použít parametr překladače -fno-move-loop-invariants, který zabrání přehazování konstantních výrazů z těla cyklu mimo cyklus (což o něco zhorší optimalizaci, ale nevyžaduje to alokaci dalších registrů pro pomocné proměnné, což zřejmě spouští onu chybu. Na můj podnět otevřel Richard Falk na Bugzille novou chybu a minimalizoval testcase. Upozornil mě, že existují i výjimečné případy, kdy ani parametr -fno-move-loop-invariants nepomůže. Pak je asi lepší použít starší GCC 4.7.2.
27.7.2017 Tak to vypadá, že v novém AVR-GCC 8.0.0 byla konečně opravena interní chyba push_reload. Dříve testované zdrojové kódy se nyní přeloží bez problémů i bez přepínače -fno-move-loop-invariants a generuje o něco menší kód. Začátkem roku 2018 se objevila nová verze AVR-GCC 8.0.1 fungující též bez problémů.
Také bych upozornil, že v AVRLibC verze 1.8.0 byly odstraněny deprecated datové typy prog_* (např. prog_char) pro umístění "proměnné" do programové paměti. Místo staré deklarace const prog_char msg[] = "AHOJ"; se používá makro PROGMEM při deklaraci "proměnné" takto: const char msg[] PROGMEM = "AHOJ"; (nelze použít v definici datového typu pomocí typedef, ale aplikuje se na konkrétní "proměnnou". Pokud nechcete překopávat staré zdrojáky, přidejte do Makefile parametr překladače -Wno-deprecated-declarations -D__PROG_TYPES_COMPAT__.
23.9.2019 Otestoval jsem novou verzi AVR-GCC 9.2.0 + binutils 2.32, překládá bez problémů, ale generuje znatelně větší kód. Např. malý prográmek pro ATtiny13A (kompilovaný s parametrem -Os) se ze 766 B zvětšil na 1000 B (proti binárce z AVR-GCC 8.0.1 + binutils 2.30.51). Navíc tento toolchain už nelze spustit pod Windows 9x (ani s KernelExem). Podobné výsledky jsem měl i s AVR-GCC 10.0.0. Další konkrétní příklad velikosti binárky z různých verzí AVR-GCC: 8.0.1 - 13408B, 8.2.0 - 13370B, 9.2.0 - 13682B, 10.0.0 - 13722B. Aktualizoval jsem tedy jen o krok dále na verzi 8.2.0. Lze ji zprovoznit i pod Windows 9x, stačí nahradit knihovnu libwinpthread-1.dll starší verzí třeba z MinGW32 (29.3.2015). U této verze GCC jsem zas později narazil na podivnou chybu při linkování, kterou opravuje verze 8.3.0.
23.10.2019 POZOR na sníženou šumovou imunitu krystalového oscilátoru u nové řady ATmegaxxxPB! V jednom existujícím zařízení jsme zvažovali výměnu staršího MCU ATmega328P za novější ATmega328PB. Nová verze PB přináší řadu rozšíření a vylepšení, např.: druhý UART, I2C a SPI, dva nové 16-bitové timery TC3 a TC4, více PWM kanálů, QTouch Peripheral Controller, CFD (Clock Failure Detection)... Pinově je více méně kompatabilní, akorát význam pinu 3 se mění z GND na PE0 (SDA1/ICP4/ACO/PTCXY) a pin 6 se mění z VCC na PE1 (SCL1/T4/PTCXY). Po resetu jsou tyto piny jako vstupy, takže mohou být připojené na GND resp. VCC beze změny PCB layoutu. Po softwarové stránce by měla být novější verze zpětně binárně kompatabilní, tzn. kód zkompilovaný pro starší verzi P může beze změny běžet na verzi PB, více informací o rozdílech v aplikační nótě od Atmelu.
Bohužel nová verze nepřináší jen samá pozitiva a jistoty, ale také byla např. odstraněna volba hodin full swing crystal oscillator (0,4 - 16 MHz, rozkmit napětí 0 - VCC) doporučovaná pro náročnější prostředí s výskytem silného elmag. rušení (na úkor spotřeby MCU). Nová verze PB už nabízí pro krystal pouze volby low-power crystal oscillator (0,4 - 16 MHz, rozkmit napětí datasheet neuvádí) a low frequency crystal oscillator (32,768 kHz). To m.j. znamená, že pro běh MCU na vyšší frekvenci než 16 MHz, je nutný externí generátor hodin místo krystalu. Nová funkce CFD má zajistit, že v případě výpadku hodin z externího krystalového oscilátoru dojde automaticky k přepnutí na vnitřní hodiny 1 MHz z RC oscilátoru a signalizaci nahozením flagu, příp. vyvoláním přerušení (např. MCU řady STM8 a STM32 to už umí docela dlouho).
Chtěl jsem tuto novou funkci vyzkoušet a tak jsem nastavil příslušný bit 3 CFD v EXTFUSE a do hlavní smyčky přidal ladicí výpis při nahození flagu XFDIF v registru XFDCSR. Když jsem se pak dotknul hrotem pinzety pinu XTAL1 (vstup krystalového oscilátoru), tak se začaly dít věci! Místo předpokládané ladicí hlášky se na terminál chrlily různé shluky paznaků a program evidentně někde bloudil, občas se zacyklil a pomohl až HW reset. Nejhorší ale je, že po několika takových klovancích došlo k náhodné změně v programové paměti a MCU zůstal viset v bootloaderu. Při dalším klování se mi dokonce povedlo zničit i bootloader. Při dumpu paměti FlashROM a EEPROM jsem našel řadu náhodně modifikovaných Bytů. Tento problém jsem byl schopen celkem opakovaně vyvolat během pár desítek sekund, ať byla funkce CFD zapnutá nebo vypnutá. Při zarušení krystalového oscilátoru zřejmě dojde k náhodnému skoku v programu a ten tak může natrefit i blok kódu s instrukcí SPM, která je součástí bootloaderu, jenž pochopitelně potřebuje programovou paměť mazat a zapisovat. Když jsem do MCU nahrál jen aplikaci bez bootloaderu, tak už k přepsání FlashROM nedocházelo a když jsem z aplikace odstranil kód zapisující do EEPROM, tak nedocházelo ani k náhodnému přepsání EEPROM.
Funkce CFD zafungovala podle očekávání jen tehdy, pokud jsem se napřed jedním hrotem pinzety dotknul plošky GND a pak až druhým hrotem přišlápl pin XTAL1. Pak jsem dostal na terminálu očekávanou ladicí hlášku (bitovou rychlostí sníženou v poměru frekvence krystalu 8 MHz vůči RC oscilátoru 1 MHz). Když jsem stejný pokus opakoval na starším MCU ATMega328P s volbou low-power crystal oscillator, tak se program jen zastavil, ale nikdy nedošlo k náhodnému bloudění a přepsání paměti. Osciloskopem jsem změřil napětí na pinu XTAL1: 1,1 Vpp vs 0,88 Vpp u verze PB, což zas není tak velký rozdíl, viz níže. Toto chování nové verze PB považuju za nepřijatelné a řešení CFD jako nefunkční, errata se o ničem takovém zatím nezmiňují. Jak jsem se dočetl později, uživatelé hlásili problémy s oscilátorem už v roce 2016. Ani nechci domyslet, jaký průser by nastal, kdyby se do terénu dostaly tisíce zařízení s novým MCU a postupně pak začaly chcípat...
ATmega328P XTAL1 ATmega328PB XTAL1
UPDATE: V datasheetu k ATmega328P jsem si v sekci 40.8 Errata ATmega328P všiml zmínky onové revizi K, kde se mimo jiné zmiňuje Full swing crystal oscillator not supported! To je docela zásadní změna, která by podle mě měla být jasně odlišená jiným typovým označením. 3-Bytová signatura (0x1E, 0x95, 0x0F) zůstává beze změny, ale mění se debugWIRE ID z 950Fh na 9516h u revize K. Bohužel asi není možné toto ID přečíst programově bez debuggeru.
UPDATE2: Z diskuse s technickou podporou Microchipu nakonec vyplynulo, že zmiňovaná revize K vůbec nepůjde do produkce a zmínka o ní bude z datasheetu vymazaná. Takže alespoň u ATmega328P, která by se měla i nadále vyrábět, zůstane full swing crystal oscillator.
- = MCU-8bit: SDCC = -
V práci jsme jako levnou náhradu MCU Atmel AVR pro nenáročné aplikace začali používat 8-bitové MCU STM8S od firmy ST. Konkrétně si teď hraju na STM8S-Discovery kitu s procesorem STM8S105C6. Součástí kitu je i USB programátor a debugger ST-Link, který s MCU komunikuje přes 1-drátové rozhraní SWIM (Single Wire Interface Module). Oproti AVR má STM8 propracovanější hodinový systém (např. funkce hlídání ext. krystalového oscilátoru a přepnutí na interní RC v případě výpadku), řadič vnořených přerušení s 4 úrovněmi priorit, rychlý ADC (14 tiků na převod), PWM časovač s komplementárním výstupem a programovatelným dead-time pro buzení můstku, instrukce pro 8 a 16-bitové dělení (16 tiků). Z GPIO pinů lze tahat jen 20 mA (AVR dá 40 mA), ale na LEDky to stačí. Max. hodinový takt je 16 MHz. Vyrábí se i v nohovém pouzdru SDIP32, avšak se zmenšenou roztečí 70 milů.
Pro rodinu STM8 je k dispozici řada komerčních překladačů C, např. IAR Embedded Workbench for STM8, Raisonance STM8 C Compiler, Cosmic STM8 Cross Development Tools. Já jsem ale hledal spíše něco malého, opensourcového, podobného GCC a kolega mi poradil SDCC (Small Device C Compiler), který tyto požadavky splňuje. Jedná se o OpenSource multiplatformní projekt - překladač lze provozovat na Windows, Linuxu (x86, x64, ARM, PPC, SPARC), Solarisu, Mac OSu... a podporuje celou řadu menších MCU, např. řady x51, 80DS390/400, Z80, Z180, Rabbit 2000, GBZ80, 68HC08, S08, STM8, TLCS90 a částečně PIC16 a PIC18. SDCC podporuje většinu rozšíření C99 i některé C11. Překladač lze pouštět buď přes nějaké IDE nebo z příkazového řádku pomocí Makefile skriptu či dávkového souboru, to už je na vás. Standardně produkuje zdrojový kód v assembleru proložený řádkami C kódu jako komentáře, z kterého si lze snadno udělat představu, jak optimální kód generuje a dále LST, MAP a další pomocné soubory a strojový kód v intel HEX formátu. Podrobněji se SDCC a optimalizacím věnuje ve svém článku Marrek. Kód do MCU nahrávám po USB pomocí programu ST Visual Programmer, který existuje v GUI i CLI verzi. Pro Linux lze použít jednoduchý OpenSource program STM8Flash.
Uvedu zde 3 specifické konstrukce, bez kterých se embedded programátor neobejde. Všechny typy pamětí (RAM, EEPROM, Option Bytes, Flash) a registry jsou mapovány do jednoho 24-bitového adresního prostoru (program counter je též 24-bitový, ale v současnosti se využívá jen 16-bitů) a lze je nadefinovat pomocí ukazatelů na danou adresu např. takto:
#define TIM1_CNTR (*(volatile uint16_t *)0x525E) // TIM1 Counter (16b) #define TIM1_CNTRH (*(volatile uint8_t *)0x525E) // Data bits High #define TIM1_CNTRL (*(volatile uint8_t *)0x525F) // Data bits Low #define TIM1_PSCR (*(volatile uint16_t *)0x5260) // TIM1 Prescaler register (16b) #define TIM1_PSCRH (*(volatile uint8_t *)0x5260) // Data bits High #define TIM1_PSCRL (*(volatile uint8_t *)0x5261) // Data bits Low
Klíčové slovo volatile zajistí, aby překladač i při optimalizaci četl vždy aktuální hodnotu registru, kdykoliv je v nějakém výrazu použita. STM8 si poradí s atomickým čtením 16-bitového registru pomocí instrukce LDW a dvou 16-bitových registrů X, Y.
Obsluha přerušení (ISR) se v SDCC deklaruje pomocí klíčového slova __interrupt (IRQn) za hlavičkou funkce, kde IRQn je číslo přerušení (viz datasheet ke konkrétnímu MCU). Překladač vygeneruje funkci bez prolog/epilog kódu (obdoba __attribute__((naked)) u GCC) ukončenou instrukcí IRET a pokud je uvedeno u číslo přerušení, tak zapíše ukazatel na ISR do tabulky vektorů přerušení. Pozor na to, že jedno přerušení může být vyvoláno více událostmi v HW. Např. přerušení přijímače UARTu může mít až 6 různých zdrojů, které lze jednotlivě maskovat v konfiguračních CRx registrech UARTu. ISR pak musí zjistit podle stavových bitů, co vlastně bylo příčinou vyvolání přerušení. Pokud je povolen pouze jediný zdroj přerušení, tak se to řešit nemusí. Při volání ISR MCU automaticky uloží na zásobník registry: PC, Y, X, A, CC. Časově to trvá: 1-6 cyklů na dokončení aktuální instrukce + 9 cyklů pushnutí + 9 cyklů na popnutí registrů. Ve výchozím stavu mají na STM8 všechna přerušení nastavenou nejnižší prioritu 3 a frontují se. Pokud prioritu některého přerušení zvýšíme zápisem 2-bitové hodnoty do registrů ITC_SPR1-8, bude moci přerušit jinou právě probíhající ISR s nižší prioritou.
void uart_rx_complete_isr(void) __interrupt (21) { ... }
SDCC nedefinuje žádné makro pro globální povolení přerušení, takže si ho nadefinujeme pomocí inline assembleru s klíčovým slovem __asm__ (odřádkování za jménem instrukce je tam jen pro lepší čitelnost generovaného kódu):
#define enable_interrupts() __asm__("rim\n") // global interrupts enable #define disable_interrupts() __asm__("sim\n") // global interrupts disable
Více se o programování různých periferií STM8 dozvíte např. z tutoriálů zde.
- = MCU-32bit: ARM-GCC = -
Vývoj na poli MCUček prošel během pár desetiletí od ranné fáze 8-bitových jednapadesátek a PICů programovaných v assembleru přes 8-bitová AVRka programovaná hojně v Céčku až po 32-bitová MCUčka programovaná v C, C++ i vyšších jazycích. Výkon a stupeň integrace vzrostl o několik řádů a cena naopak řádově klesla, takže dnes koupíme mnohem rychlejší 32-bity za cenu předchozích 8-bitů (a díky nešťastné cenové politice Atmelu dokonce ještě mnohem levněji - např. zde a zde, jak předražená AVRka). 8-bity mají u mě pořád své místo, protože pro spoustu aplikací pořád vyhoví (netřeba chodit s kanónem na vrabce), jsou jednoduché na programování a mám dost šuplíkových zásob na bastlení. Avšak je třeba se poohlédnout dále...
Nejrozšířenější 32-bitové MCUčka jsou dnes založená na RISCové architektuře ARM, která vnikla už v 80. letech minulého století a rozvinula se v obrovské a nepřehledné množství variant od malých MCUček po vícejádrové 64-bitové aplikační procesory pro mobily, tablety a low-power servery. Něco s ARM jádrem najdete skoro v každé spotřební elektronice. Další alternativy jsou třeba MIPS, PowerPC, RISC-V (spíše velké aplikační procesory) nebo CISC Renesas RX (menší MCUčka). Jde o poměrně novou architekturu navrženou od základu, která za sebou nevláčí žádný balast kompatability jako ARMy a má tak jednodušší a patrně efektivnější instrukční sadu i jednodušší periferie. ARMová MCUčka vyrábí celá řada výrobců, kteří si k převzatému ARM jádru lepí vlastní periferie, např. ST, NXP (které se spojilo s Freescale), Nordic, či Cypress. Ten je zajímavý tím, že na své čipy dává i programovatelnou logiku (PLD), jenž umožňuje tvorbu vlastních periferií na míru.
V práci z historických a cenových důvodů používáme ARMy od ST, v současnosti řady STM32F0xx, STM32F10x, STM32F20x, STM32F4xx, STM32F7xx a nově též STM32L4xx. Na hraní jsem dostal vývojový kit NUCLEO-L476RG s MCU STM32L476RGT, který obsahuje jádro ARM Cortex-M4 na max. 80 MHz, 1 MB FlashROM, 128 kB SRAM, 14-kanálové DMA, TrueRNG, 3x 12-bit ADC, 2x 12-bit DAC, USB OTG 2.0, 6x USART, 3x I2C, 3x SPI, CAN a další periferie. Napájení může být v rozsahu 1,71 - 3,6 V a podporuje řadu úsporných režimů. Kit obsahuje pouze základní minimum - 3,3V a 5V stabilizátor, 32kHz LSE krystal (lze doosadit i HSE krystal X3 - dal jsem si tam 8 MHz a k němu je nutno dopájet kondíky C33, C34 - dal jsem tam 18 pF a propojky na místo R35, R37 a naopak ze spodní strany odpájet propojky SB54, SB55 - odpojí se tím porty PH0, PH1 od hřebínku), LEDku a 2 tlačítka (jedno je RESET). Většina pinů procesoru je vyvedena na pinové hřebínky kompatabilní s Arduinem (možno připojit různé shieldy).
Důležitou součástí kitu je on-board ST-LINK/V2-1 programátor a debugger s rozhraním SWD (zjednodušený JTAG po 2 drátech SWDIO a SWCLK) řízený samostatným MCU STM32F103, který ST bundluje ke každému svému kitu. Deska je rozdělená na 2 části spojené tenkými můstky, takže ST-LINK lze odlomit a používat k programování jiných MCU skrze konektor CN4 (1 - Vref, 2 - SWCLK, 3 - GND, 4 - SWDIO, 5 - NRST, 6 - SWO) jako samostatný programátor ST-LINK (na eBay lze koupit i levnější verzi). Resp. není ani potřeba ho odlamovat, stačí vytáhnout 2 jumpery z CN2, odpájet zespodu nulový odpor SB12 a přehodit R23 (4k7) na místo neosazeného R9. V případě nestabilní komunikace s targetem může pomoci zvýšení hodnoty odporů R5, R8, R13 z 22 Ω na cca 150 Ω. ST-LINK se připojuje k PC přes rozhraní USB a tváří se jako složené zařízení obsahující Mass Storage (sem stačí nakopírovat zkompilovanou binárku a ST-LINK ji ihned napálí do MCUčka), virtuální sériový port pro komunikaci s MCU přes UART2 a vlastní STLink dongle.
Programovací nástroj ST-Link Utility je ke stažení zdarma, akorát se musíte na webu ST zaregistrovat. Balíček obsahuje ovladače na výše zmíněná USB zařízení, aktualizaci FW on-board ST-LINKu, MCU FlashLoadery a programovací aplikace (s GUI a pro příkazovou řádku). Z nějakého důvodu mi pod Windows XP SP3 nechtěl fungovat ovladač VCP - soubory (v podstatě jen INF a CAT soubor, neboť využívá systémový usbser.sys) se nainstalovaly, ale ve správci zařízení hlásil chybu "Toto zařízení nelze spustit. (Kód 10)". Nepomohla aktualizace FW ani USB VCP ovladače. Nakonec jsem zjistil, že byl problém v mých XPčkách, kde jsem měl nainstalovanou nekompatabilní verzi usbser.sys z Windows 2003 Serveru a po vrácení verze z XP SP3 už je vše OK. Poslední verze ST-Link Utility funkční pod Windows XP je 4.5.0 z roku 2019. O rok později byl vývoj ukončen poslední verzí 4.6.0 a nahrazen mastodontí javasračkou STM32CubeProgrammer, která je však nutná pro nové MCU např. řady STM32H7xx. Ještě existuje OpenSource commandline verze ST-Linku, ale poslední release 1.7.0 z roku 2021 už také není zrovna nejčerstvější.
Linuxáci (nejen) můžou použít univerzální OpenSource nástroj OpenOCD (zde a zde jsou binárky pro Win32/64/Linux/MAC OS), který podporuje i ST-LINK/V2 a V3. OpenOCD funguje i jako GDB server, takže se na něj lze přes GDB připojit a ladit program. ST-LINK je také možno přeflashnout FW J-Linku a používat pro ST kity SW od Seggeru. Ačkoliv to Segger přímo nedovoluje, povedlo se mi přes ST-LINK naprogramovat i Nordic nRF51822 pomocí utility nrfjprog.exe.
Další možností, jak do MCU dostat program, je použít vestavěný ROM bootloader. Aktivuje se kombinací pinů BOOT0, BOOT1 a pulsem na pinu RESET#. Umí komunikovat po UARTu, CANu, SPI, I2C a USB DFU (záleží na konkrétní řadě STM32), takže už se vám nestane, že byste si MCU bricknuli jako to šlo třeba u AVRek. Akorát si při návrhu HW dejte pozor, co je na kterém rozhraní za periferii, bootloader se totiž chytí tam, kde uslyší první výzvu. Program je možné nahrát jak do FlashROM, tak do interní SRAM (hodí se třeba pro ladění, když nechceme trápit flešku neustálým mazáním), rozdíl je pouze v adrese. Pro komunikaci lze použít buď program STM32 Flash loader pro Windows nebo OpenSource command line utilitu stm32flash pro Windows a Linux.
Asi nejlevnějším ARM kitem je Blue Pill s malým MCU STM32F103C8T6 (72 MHz, 64/128 kB FlashROM a 20 KB SRAM, LQFP48) běžně dostupný na AliExpressu a eBay (kupoval jsem za 48 Kč), který se hojně rozšířil díky podpoře v Arduino IDE. Obsahuje pouze MCU, krystaly, LED, tlačítko, 3,3V LDO, a microUSB konektor. Programovat se dá přes SWD header např. z výše zmíněného ST-Linku. Později se na kitech vyrojila celá řada klonů a fejků originálního MCU od ST. Některé jsou vyloženě odpad skrytý pod logem ST, na kterém neběží ani jednoduchý program, jiné celkem fungují. Zajímavý je třeba klon GD32F103RBT6 od GigaDevice, který m.j. nabízí i seriózní distributoři součástek jako Mouser, jenž obsahuje 2 čipy - MCU jádro a SPI FlashROM z které se po resetu nahraje program do větší SRAM a běží tak rychleji díky přístupu s 0 čekacími stavy (max. f je 108 MHz), ale za cenu vyšší proudové spotřeby. Také existuje varianta s RISC-V jádrem GD32VF103. Pro identifikaci a testy MCU na kitu byla dokonce vyvinuta utilita Bluepill Diagnostics.
Dále se podíváme jaké jsou k dispozici překladače a knihovny. Komerčních kompilátorů je celá řada, ST přímo podporuje IAR Embedded Workbench a Keil MDK-ARM, jejichž trial/free verze jsou limitované velikostí generovaného kódu (u IARu navíc 30-denní zkušební dobou). Na bázi OpenSource Eclipse IDE, ARM-GCC a dalších utilit upeklo ST svůj mastodontí javoprasobastl System Workbench, který však nepoužívá standardní Makefile, ale projektové soubory Eclipse .cproject a .project. Resp. Makefile soubory z daného projektu generuje až při překladu, navíc s hardkódovanými absolutními cestami, takže nejsou moc použitelné. Zde je podrobný návod, jak si zprovoznit vlastní instalaci Eclipse s GNU toolchainem a GDB. Pro menší projekty si můžete vyzkoušet webové IDE mbed Compiler od ARMu, které není nijak omezené, stačí free registrace. Data máte uložená v cloudu, stáhnete si akorát zkompilovanou binárku (příp. balíček se zdrojáky) a nahrajete do kitu.
Jelikož jsem zvyklý v práci i doma používat GNU toolchain bez zbytečných serepetiček, vydal jsem se touto cestou i zde. Balík ARM-GCC je volně ke stažení pro Windows, Linux a MAC OS. Novější ARM-GCC 9.x je zde. K němu je potřeba ještě doplnit CoreUtils, make a případně další GNU utility (možno použít i ty z MinGW, pokud ho už máte nainstalované). Celá instalace spočívá jen v rozbalení toolchainu a přidání cesty do adresáře bin. ARM pro svá Cortex jádra poskytuje zdarma balíček hlavičkových souborů a DSP knihoven CMSIS (Cortex Microcontroller Software Interface Standard). Nás budou zajímat zejména hlavičkové soubory s definicemi registrů pro dané jádro nacházející se v adresáři CMSIS\Core\Include, např. pro Cortex-M4 je to soubor core_cm4.h a z něj inkludované pomocné soubory cmsis_*.h. K tomu budeme potřebovat minimálně ještě další hlavičkové soubory s definicemi registrů specifických periferií, které už jsou závislé podle výrobce a konkrétního typu MCU.
ST nabízí zdarma 2 typy mastodontích knihoven, jejichž součástí je i CMSIS. Starší a menší Standard Peripherals library už nepodporuje nové typy MCU, jako např. STM32L4xx, takže do budoucna se asi nemá cenu jí zabývat. Novější a větší balík STM32Cube (je potřeba stáhnout odpovídající verzi pro danou rodinu STM32, např. pro STM32L4xx má rozbalený asi 600 MB) obsahuje i FreeRTOS a spoustu příkladů ke kitům. Bohužel jak už jsem zmínil výše, nenajdete tam nikde Makefile pro GCC, ale pouze projekty pro IAR, Keil a System Workbench. Dále obsahuje důležité hlavičkové soubory pro konkrétní typy MCU, které najdeme v adresáři Drivers\CMSIS\Device\ST\STM32L4xx\Include (např. pro STM32L476RGT budu potřebovat soubory stm32l4xx.h, system_stm32l4xx.h a stm32l486xx.h - ten se inkluduje automaticky podle konstanty STM32L486xx definované v uživatelském programu nebo v Makefile), assemblerovské start-up soubory v adresáři Drivers\CMSIS\Device\ST\STM32L4xx\Source\Templates\gcc (např. pro STM32L476RGT je to startup_stm32l486xx.s) a jeden zdrojový soubor Drivers\CMSIS\Device\ST\STM32L4xx\Source\Templates\Templatessystem_stm32l4xx.c, který obsahuje defaultní implementace systémových funkcí pro nastavení hodin SystemInit() (spouští se ještě před main() ze start-up souboru), SystemCoreClockUpdate() a globální proměnnou SystemCoreClock, ale je to napsané prasácky a bude lepší si to přepsat po svém. Najdeme zde i hlavičkové soubory z CMSISu v adresáři Drivers\CMSIS\Include.
Pro ovládání periferií nabízí STM32Cube 2 možnosti: přes LL (Low-level Library) a HAL. Pro usnadnění generování inicializačních kódů je navíc možno použít další javamastodont STM32CubeMX, v kterém si lze pohodlně naklikat použité periferie, přiřadit je pinům a program vygeneruje template projekt s inicializačními kódy a hlavičkovými soubory buď z LL nebo HAL. Baoshi napsal nějaký pythoní skript, který by měl pro takto vytvořený template vygenerovat Makefile, ale nefungoval mi, protože v projektových souborech nemohl najít nějaké definice - asi se v novější verzi STM32CubeMX změnil formát generovaných projektů. Nicméně obě možnosti LL i HAL mi přijdou jako poměrně velký overhead (desítky kB zdrojáků na konfiguraci blbého GPIO), takže jsem je zatím nevyužil a držím se jen hlavičkových souborů.
Nakonec jsem si musel napsat Makefile sám s inspirací z Internetu a podle mého Makefile, který používám pro AVR-GCC. Na začátku mám definovaný název projektu, seznam modulů, typ MCU, relativní cestu k CMSISu, dále seznam potřebných nástrojů, start-up soubor a linker skript, parametry pro GCC a linker, a nakonec jednotlivá pravidla pro kompilaci, konverzi objektového souboru AXF do binárky, hexu a listingu a také pro flashnutí a debug přes OpenOCD + GDB. Linker skript jsem použil beze změny z STM32CubeL4: Projects\STM32L476RG-Nucleo\Templates\SW4STM32\STM32L476RG_NUCLEO\STM32L476RGTx_FLASH.ld. Obsahuje definice jednotlivých sekcí paměti pro uložení kódu, dat, IVT a nastavení zásobníku a heapu. Zde si můžete stáhnout můj kompletní projekt: BLINK476.ZIP [195 kB] s primitivním blikáním LEDky. Ano, rozblikat LEDku, to je vždycky základ :) Dál už se "jen" zbývá prokousávat reference manuálem, který má přes 1700 stran. Nějaké použitelné turotiály najdete zde.
10.2.2017 Minimálně pro začátek, než si člověk rozběhá vlastní ladicí systém, se může hodit nějaký debugger. V komerčních IDE je obvykle už nějaký debugger zabudován. V GNU toolchainu je k dispozici GDB (arm-none-eabi-gdb.exe) s primitivním konzolovým rozhraním. GDB umožňuje ladit lokálně program z disku či běžící proces nebo se připojit ke vzdálenému GDB serveru přes síť nebo sériovou linku. GDB serverem může být třeba výše zmíněné OpenOCD nebo Segger J-Link GDB Server a další. Pro pohodlnější ovládání GDB by se hodil nějaký front-end. Většina jich je pro Linux. Pro Windows toho není tolik, navíc bych se rád vyhnul molochům typu Eclipse či WinGDB, který potřebuje M$ Visual Studio. Naopak sháním něco malého a lehkého. Prozkoumal jsem tedy další možnosti a zde nabízím stručný přehled:30.3.2017 Po rozblikání LEDky jsem se postupně prokousal nastavením hodinového systému, UARTu, SPI, definicí obsluhy přerušení a napsal si vlastní jednoduché knihovny. A tak přišel čas zkusit připojit malý OLED displej, který jsem si loni koupil na hraní z eBay za 78,- Kč. Displej je bílý monochromatický s rozlišením 128x64 o aktivní úhlopříčce 0,96". Je osazený řadičem SSD1306, který ve výchozí konfiguraci komunikuje jednosměrně přes SPI rozhraní (D0=SCK, D1=MOSI, CS=SS#) s pomocnými signály DC (Data/Command#) a RES (RESET#). Pomocí SMD odporových propojek na zadní straně PCB je možné nakonfigurovat též 9-bitové SPI bez potřeby signálu DC nebo I2C. Max. frekvence hodin SPI je 10 MHz a napájecí napětí je 3,3 - 5 V.
- Affinic Debugger pro Windows, Linux a Mac OS X (Lite verze je zdarma) - instalace zabírá cca 76 MB, nelze spustit pod Windows XP kvůli chybějící funkci inet_pton v knihovně WS2_32.dll (ani starší verze nejde), takže jsem ho nevyzkoušel.
- DDD (Data Display Debugger) pro Linux a Windows (Cygwin port) - potřebuje k funkci rozsáhlý subsystém Cygwin/X (X Windows) a řadu dalších podpůrných knihoven (celá instalace tak má přes 500 MB. Navíc s Cygwinem je potíž, protože od verze 2.6.0 z konce roku 2016 přestal podporovat Windows XP. Poslední podporovanou verzi 2.5.2 lze nainstalovat podle tohoto návodu. Po spuštění Cygwinu dávkou Cygwin.bat je potřeba v shellu nejprve spustit X Windows příkazy:
export "DISPLAY=:0"
XWin.exe -multiwindow -emulate3buttons &
a pak teprve spustit DDD:
ddd.exe --eval-command="target remote localhost:3333" BLINK-CM4.axf
kde BLINK-CM4.axf je zkompilovaný program s ladicími informacemi (při překladu pomocí gcc je potřeba použít parametr -g) a 3333 je číslo portu, na kterém poslouchá GDB server (v openocd.cfg definovaný řádkem gdb_port 3333. Po chvíli laborování se mi DDD připojil ke GDB serveru, načetl zdroják, disassemblovaný kód a obsah registrů, ale chovalo se to celé nějak divně. Krokování programu fungovalo jakžtakž v okně assembleru (v okně zdrojáku krokovat nešlo), nedařilo se mi program zastavit a znovu spustit, ani nastavit breakpointy. Vypadalo to, že je nějaký problém mezi komunikací DDD a GDB klientem, tak jsem to nakonec vzdal.- CGDB pro Linux a Windows (Cygwin port) je textový front-end postavený na knihovně ncurses - stejné potíže jako s Cygwiním DDD portem výše.
- UltraGDB pro Linux a Windows je nějaká čínská verze okleštěné Eclipsy, takže potřebuje pro běh Javu, velikost instalace je cca 88 MB - ladění probíhá ve zdrojáku, po komplikovanějším nastavování projektu se sice připojil ke GDB serveru, načetla se paměť programu a registry, ale opět vykazoval podobné potíže jako DDD výše. Po rozběhnutí programu se mi ho už nepodařilo stopnout a dostal jsem akorát chybovou hlášku "Remote communication error. Target disconnected.: Invalid argument."
- IDA Pro pro Windows, Linux a Mac OS X je klasický disassembler používaný hlavně pro reverse engineering binárek, ale jak jsem zjistil, tak podporuje i živé ladění pomocí vlastního GDB plug-inu. K běžícímu GDB serveru se lze připojit přes menu "Debugger|Attach|Remote GDB debugger", kde je potřeba nastavit adresu a port a dále v "Debug options|Set specific options" vybrat procesor ARM. IDA debugger lze spouštět i z příkazové řádky (volat z Makefile) pomocí příkazu:
idag.exe -rgdb@localhost:3333+ BLINK-CM4.axf
IDA umí pouze ladění v assembleru (s využitím jmen symbolů z binárky), ale tady to aspoň fungovalo.- emIDE pro Windows je kompletní OpenSource IDE založené na Code::Blocks s podporou ladění přes GDB a pluginem pro Segger J-Link. emIDE lze použít i pro vývoj na Renesas RX a dalších platformách pro které je k dispozici GNU toolchain. Velikost po rozbalení je cca 233 MB (bez arm-gcc toolchainu), ale z toho většinu zabírají definiční soubory pro MCU Atmel SAM, dá se to zkrouhnout na pouhých 12 MB. Nastavení projektu se ukládá do vlastního souboru s příponou .emp (textový XML formát), avšak v rámci projektu umožňuje též volat externí Makefile soubor. Nechybí podrobné nastavení parametrů překladu. Ladění probíhá standardně ve zdrojáku a lze zapnout i okno s assemblerem. Zkoušel jsem to s OpenOCD i J-Link GDB Serverem a vypadá to, že to funguje, doporučuji vyzkoušet.
- Segger Ozone pro Windows, Linux a Mac OS X je vizuální debugger, který není založený na GDB, ale používá vlastní J-Link Remote Server (je potřeba ST-Link přeflashovat na J-Link, jak bylo popsáno výše - pro nekomerční použití zdarma), velikost instalace je cca 50 MB. Ladění probíhá v okně se zdrojákem, kde pod každou řádkou kódu lze rozkliknout blok v assembleru a funguje bez problémů, doporučuji vyzkoušet.
Co mě na těhle řadičích displejů mrdá, je způsob mapování datové RAM na pole pixelů. Po sobě zapisované Byty se totiž nezobrazují normálně po řádcích, ale po svislých osmicích pixelů, které se postupně zobrazují zleva doprava. V konfiguraci řadiče je možné nastavit horizontální či vertikální otočení obrazu nebo vertikální režim, kde se po sobě zapisované Byty zobrazují souvisle ve sloupcích. Abych nemusel předělávat své vykreslovací funkce, vyřešil jsem to transformací, která při posílání Bytů z off-screen bufferu na displej transponuje matici 8x8 bitů.
6.2.2018 Po aktualizaci ARM-GCC 6.3.1 na verzi 7.2.1 mě čekalo nepříjemné překvapení: nově zkompilovaný kód byl sice o pár set Bytů menší, ale nefungoval. Zjistil jsem, že mi LTO optimalizátor vyházel kód obsluh přerušení (patrné z LST souboru, kde nebyly na ISR žádné reference) a dané vektory byly přesměrovány na Default_Handler (patrné z MAP souboru), který jen zacyklí procesor v nekonečné smyčce. Pro zmenšení výsledné binárky používám parametry překladače CFLAGS+=-Os -flto -ffunction-sections -fdata-sections a LDFLAGS+=-Wl,--gc-sections. Pokud parametr -flto zruším, vše funguje správně, akorát je výsledný kód v daném případě asi o 1 kB větší. Snažil jsem se vypuštění kódu ISR zabránit pomocí atributů interrupt, used, externally_visible a section(".isr_vector"), kterážto sekce je v linker skriptu definovaná jako KEEP(*(.isr_vector)), ale nic z toho napomohlo. Tak jsem se zatím vrátil k ARM-GCC a 6.3.1 napsal bugreport.
2.3.2018 Na stejný problém narazilo více uživatelů a Bernd Kreuss mě upozornil, že tato chyba už byla dříve reportována zde a popsal jednoduchý workaround. Stačí v Makefile prohodit pořadí linkovaných objektů tak, aby první na příkazové řádce byl assemblerovský start-up soubor a až za ním Céčkové objekty. Konkrétně v mém případě jsem prohodil proměnné OBJS a STARTUP na řádce v Makefile takto: $(NAME)-$(CORE).axf: $(STARTUP) $(OBJS). Pak už vše fungovalo normálně, kód ISR nebyl vyhozen. Ani jsem nemusel použít pro deklaraci ISR žádný speciální atribut. UPDATE1: poslední aktualizace ARM-GCC 9-2020-q2 (9.3.1) chybu stále obsahuje. UPDATE2: chyba už byla opravena commitem do binutils 2.35, v současném toolchainu se zatím používá předchozí verze 2.34, tak se snad už brzy dočkáme. UPDATE3: v balíčku ARM-GCC 10.3-2021.10 (10.3.1) je už chyba konečně opravena.
Tipy a triky pro STM32
V průběhu sžívání se s novou architekturou MCU člověk běžně narazí na různé záseky. A u tak složitých MCU, jako jsou STM32, o ně není nouze...
- Po resetu mají periferie MCU ve výchozím stavu vypnuté hodiny. Abychom mohli periferii vůbec začít konfigurovat a používat, je nutné napřed hodiny zapnout. V případě GPIO se to dělá nastavením příslušného bitu v registru RCC->AHB2ENR (podle toho, na kterou AHB jsou v konkrétním MCU GPIO připojené). Pak je nutno počkat alespoň 1 AHB cyklus před zápisem do konfiguračních registrů periferie. Pokud nepočkáme, tak se první zápis ignoruje. V Céčku to lze zapsat následovně:
RCC->AHB2ENR|=RCC_AHB2ENR_GPIOBEN; // enable AHB2 clock for GPIOB (void)RCC->AHB2ENR; // wait 1 AHB2 cycle (dummy read) for change being valid GPIOB->MODER&=~3UL; // set PA.0 mode to digital input
- Při programování a ladění MCU přes SWD se vám může stát, že si omylem předefinujete GPIO, kde se nachází piny SWDCLK a SWDIO na něco jiného a tím si ukázkově pod sebou podříznete větev :) To se dá snadno vyřešit připojením RESET# pinu MCUčka k ST-LINKu (na Nucleo boardech je to propojené) a v ST-LINK Utility zapnutím volby v menu "Target|Settings...|Mode=Connect Under Reset" a "Reset Mode=Hardware Reset". V OpenOCD je potřeba do konfiguráku přidat/upravit nastavení resetu řádkou reset_config srst_only srst_nogate connect_assert_srst, která shodí RESET# pin do log. 0 a následně řádka reset halt ho vrátí do klidové úrovně log. 1.
- Low-power režimy a správné nastavení portů: STM32 podporují celou řadu úsporných módů. Můžeme též podle potřeby dynamicky měnit PLLkem frekvenci hodin Cortex jádra a děličky AHB, APB. Doporučuji podrobněji nastudovat v reference manuálu. Základní je Sleep mode, v němž se vypnou hodiny Cortex jádra. Do něj přejdeme instrukcí WFI (Wait For Interrupt) nebo WFE (Wait For Event). V cmsis_gcc.h jsou definovaná příslušná makra __WFI() a __WFE(). Stav registrů a paměti zůstává zachován, periferie běží. Spotřeba klesne na pár mA. K probuzení dojde při přerušení nebo události, dříve vybraný oscilátor zůstne zachován. Událost se liší tím, že nemá žádný vektor, kam by program po probuzení skočil.
Další je Stop mode, kdy se zastaví hodiny v celé 1,8V doméně včetně PLL, HSE i HSI oscilátorů. Stav registrů, paměti a GPIO zůstává zachován. Spotřeba klesne na pár desítek µA. Přejde se do něj nastavením specifických řídicích registrů. Je možno též přepnout vnitřní LDO pro napájení jádra procesoru do úsporného módu. Následující ukázka kódu je pro STM32F0x0:
DBGMCU->CR|=DBGMCU_CR_DBG_STANDBY_Msk; // enable debugging in standby mode SCB->SCR|=SCB_SCR_SLEEPDEEP_Msk; // set deep sleep flag PWR->CR|=PWR_CR_PDDS_Msk; // set power down mode: (0=stop, 1=standby) PWR->CR|=PWR_CR_LPDS_Msk; // set CPU core voltage regulator to low power mode __WFI(); // wait for interrupt
Nejnižší spotřeby lze dosáhnout ve Standby mode, kdy se vypne 1,8V napájení jádra, PLL, HSE i HSI oscilátory. Obsah vnitřní SRAM a registrů není zachován vyjma RTC domény. GPIO se přepnou do stavu vysoké impedance vyjma některých portů. Spotřeba klesne na pár µA. Probudit se z něj lze jen resetem, watchdogem, RTC nebo vybranými WKUP piny, oscilátor se přepne na HSI.
Důležitou podmínkou pro úspěšné dosažení nízké spotřeby je, aby všechny GPIO porty MCU měly buď pevně definovanou logickou úroveň (vnitřní/vnější pull-up/down či ext. obvod) nebo byly přepnuty do analogového režimu (režim 3 v registrech GPIOx->MODER. To proto, že jsou vstupy vybavené Schmiťáky s vysokou vstupní impedancí a u nezapojených pinů se můžou vlivem okolního elmag. pole různě nedefinovaně překlápět. U MCU řady STM32Fxxx je výchozí režim GPIO digitální vstup, což právě může způsobovat tyto problémy. Proto jsem je preventivně na začátku programu před nastavováním dalších GPIO všechny kromě SWD pinů přepnul do analogového režimu, v němž jsou Schmiťáky odpojeny. U novější řady STM32Lxxx je už výchozí režim GPIO analogový, takže tam je to v pohodě.
- Systém přerušení: abychom mohli MCU probudit ze spánku, je potřeba ještě před usnutím nějaké přerušení nakonfigurovat a povolit. Samotné jádro ARM Cortex obsahuje NVIC (Nested Vectored Interrupt Controller), který má spoustu vstupů s konfigurovatelnou prioritou. U MCU STM32 je doplněn ještě EXTI (EXTended Interrupts and events controller), který zpracovává externí vstupy z každého GPIO pinu a sdružuje je do několika málo výstupních linek připojených na vstup NVICu. Periferie připojené přímo na NVIC tedy mají svůj samostatný vektor přerušení, zatím co skupina GPIO přes EXTI směřuje na jeden společný vektor přerušení a v ISR je pak nutno otestovat, který GPIO pin vlastně přerušení vyvolal. Před vstupem do ISR procesor automaticky uloží pár registrů. Typická latence vstupu do ISR je 16 cyklů na jádru M0, resp. 12 cyklů na na jádru M3/M4. Avšak pozor, to platí pouze pro paměť s 0 čekacími stavy, u rychleji taktovaných MCU STM32 to může být až 4 čekací stavy FlashROM. Kratší latence by tedy šlo dosáhnout zkopírováním kódu ISR do vnitřní SRAM. Přerušení pocházející z EXTI patrně budou mít delší latenci danou poměrem rychlostí AHB a APB sběrnic. Další latenci přidá kompilátor ve svém prolog kódu, který ukládá potřebné pracovní registry, někdy i ty nepotřebné. V tomto si vede lépe kompilátor IAR než GCC, nejlepšího výsledku pak dosáhneme napsáním ISR přímo v assembleru. Celková latence se tak snadno může protáhnout na 30 - 40 cyklů, řádově tedy cca 0,5 - 1µ s.
Nejprve si v reference manuálu najdeme, v kterém registru SYSCFG->EXTICR[x] se nachází náš požadovaný GPIO a vybereme ho příslušnou maskou SYSCFG_EXTICRx_EXTIy_Msk. Dále si v EXTI->RTSR vybereme, jestli chceme reagovat na náběžnou hranu, resp. v EXTI->FTSR na sestupnou hranu (či obě) a povolíme dané externí přerušení v EXTI->IMR. Pak musíme povolit sdruženou linku přerušení na vstupu NVICu inline funkcí NVIC_EnableIRQ(EXTIm_n_IRQn) a můžeme mu snížit prioritu (0 je nejvyšší, default) funkcí NVIC_SetPriority(EXTIm_n_IRQn, level). Globální přerušení je ve výchozím stavu povoleno, pro případ potřeby existují funkce __enable_irq() (instrukce CPSIE) a __disable_irq() (instrukce CPSID). Obsluhy přerušení (ISR) jsou slabě předdefinované v assemblerovském start-up souboru startup_stm32xxx.s) jako nekonečná smyčka. V našem programu si pak definujeme vlastní ISR se stejným názvem jako obyčejnou Céčkovou funkci void EXTIm_n_IRQHandler(void). Abychom zjistili, od kterého GPIO vlastně přerušení přišlo, podíváme se do registru EXTI->PR na příslušný bitflag EXTI_PR_PRy_Msk a následně zápisem 1 příslušný bitflag shodíme. Při probuzení přerušením/událostí ze Stop/Standby módu je pak potřeba obnovit některá nastavení, třeba zapnout si znovu HSE oscilátor, pokud ho používáme. POZOR, bitflag SLEEPDEEP v registru SCB->SCR zůstává po probuzení nastavený (na rozdíl třeba od flagu LPDS v registru PWR->CR), pokud tedy dále nechceme tyto režimy hlubokého spánku používat, je nutné ho ručně vynulovat (tuhle chybu jsem hledal asi 2 dny). Zde je ukázka konfigurace externího přerušení na pinu PA10 pro STM32F0x0:
SYSCFG->EXTICR[3]&=~SYSCFG_EXTICR3_EXTI10_Msk; // clear EXTI10 bits SYSCFG->EXTICR[3]|=SYSCFG_EXTICR3_EXTI10_PA; // select PORTA as source of EXTI10 - PA10 EXTI->RTSR&=~EXTI_RTSR_TR10_Msk; // disable rising edge trigger for EXTI10 line EXTI->FTSR|=EXTI_FTSR_TR10_Msk; // enable falling edge trigger for EXTI10 line EXTI->IMR|=EXTI_IMR_MR10_Msk; // enable EXTI10 line interrupt NVIC_EnableIRQ(EXTI4_15_IRQn); // enable combined EXTI4-15 interrupt in NVIC NVIC_SetPriority(EXTI4_15_IRQn, 1); // set external interrupt to lower priority __enable_irq(); // global IRQ enable void EXTI4_15_IRQHandler(void) // ISR for combined EXTI4-15 (used one time on wake up) { if (EXTI->PR&EXTI_PR_PR10_Msk) // is it EXTI line 10 ? { NVIC_DisableIRQ(EXTI4_15_IRQn); // disable combined EXTI interrupt in NVIC EXTI->IMR&=~EXTI_IMR_MR10_Msk; // disable scpecific EXTI line EXTI->PR|=EXTI_PR_PR10_Msk; // clear EXTI line 10 flag by writting 1 SCB->SCR&=~SCB_SCR_SLEEPDEEP_Msk; // clear SLEEPDEEP flag-don't enter stop mode again on WFI ... } }
Pokud potřebujeme (např. pro vykonání kritické sekce) na chvilku zakázat globální přerušení a následně ho obnovit do původního stavu (předtím mohlo být povolené i zakázané), tak je potřeba si nejprve uložit stav registru PRIMASK (na Cortex-M3 je využit pouze nejnižší bit) inline funkcí __get_PRIMASK(), který zas na konci obnovíme.
uint32_t prim; // temp. storage for PMR prim=__get_PRIMASK(); // save PMR, used only bit0: 0=enabled, 1=disabled __disable_irq(); // global IRQ disable // critical code here if (!prim) // restore previous interrupt status __enable_irq();
- Základní použití časovače: STM32 má spoustu časovačů s různými funkcemi. Dělí se na 3 základní skupiny: Advanced Timers, General Purpose Timers a Basic Timers, které se liší zejména počtem capture/compare kanálů a možností generovat DMA request. Některé časovače je možné vzájmně propojovat a synchronizovat. Já se zaměřím na 16-bitový General Purpose časovač TIM14 na STM32F030 pro načasování přesně definované pauzy. Nejprve je klasicky potřeba zapnout hodiny do příslušné periferie časovače, zde konkrétně bit RCC_APB1ENR_TIM14EN_Msk v registru RCC->APB1ENR. Pak provedeme základní nastavení časovače v registrech TIM14->CR1, TIM14->CCMR1 a TIM14->CCER, vypneme všechny přerušení od časovače zápisem 0 do TIM14->DIER a vynulujeme všechny příznaky od předchozích přerušení zápisem 0 do TIM14->SR. Pak můžeme povolit přerušení od časovače v NVICu funkcí NVIC_EnableIRQ(TIM14_IRQn). Jen dodávám, že některé časovače sdílí v NVICu stejné přerušení, takže je pak potřeba ve sdružené ISR detekovat od kterého časovače přerušení přišlo.
Dále v okamžiku, kdy potřebujeme časovač použít, nakrmíme další registry: do TIM14->PSC zapíšeme hodnotu 16-bitové předděličky, která dělí hodiny APB1 sběrnice 1:1-65536 a do TIM14->ARR zapíšeme koncovou hodnotu čítače 0-65535 při které dojde k přetečení (TIM14 čítá vzestupně). Při přetečení nastane přerušení a registr čítače TIM14->CNT se vynuluje a čítá dál. Předtím, než povolíme přerušení a spustíme časovač, je nezbytně nutné vynutit jedno přetečení čítače naprázdno (force reload) nastavením bitu TIM_EGR_UG_Msk v registru TIM14->EGR, aby se přepsaly hodnoty PSC a ARR ze stínových registrů do pracovních registrů časovače. Když jsem toto neudělal, tak při prvním spuštění časovače došlo k vyvolání přerušení skoro okamžitě, protože výchozí hodnota předděličky je 0 a teprve po prvním přetečení začal fungovat normálně. Nakonec tedy vynulujeme příznaky přerušení v TIM14->SR, povolíme přerušení od přetečení bitem TIM14->TIM_DIER_UIE_Msk v TIM14->DIER a spustíme časovač zápisem bitu TIM_CR1_CEN_Msk do TIM14->CR1. Pak už záleží na konkrétním použití, co všechno si napíšeme do ISR TIM14_IRQHandler(). Já v ní pouze otestuju bit TIM_SR_UIF_Msk v registru TIM14->SR a případně zakážu přerušení časovače vynulováním TIM14->DIER. V čekací funkci pak testuju ve smyčce, jestli bylo přerušení zakázáno jednoduchou while smyčkou. Pro delší pauzy je možné ve smyčce procesor sleepovat instrukcí WFI (pozor na to, že probuzení ze sleepu trvá nějaké takty navíc). Zde jsem narazil na jednu nepříjemnost, že procesor má podivně dlouhé latence v přístupu k registrům časovače, když je testuju ve while smyčce (bez WFI). Samotný while se přeloží na pouhé 3 instrukce, ISR má dalších 8 instrukcí a přesto mi ten while blok i při nejkratší pauze trval dlouhých 20 µs při frekvenci jádra a AHB + APB 4 MHz (z toho ISR trvá 4 µs), resp. cca 6 µs při frekvenci 16 MHz, tj. asi 96 instrukčních cyklů. Nechápu, kde se tak dlouho fláká. Pokud ve while smyčce místo registru časovače testuju nějakou lokální proměnnou, tak doba provádění kódu odpovídá předpokladu. Zatím mi tuto záhadu nikdo nevysvětlil...
// init RCC->APB1ENR|=RCC_APB1ENR_TIM14EN_Msk; // enable APB1 clock for timer 14 (void)RCC->APB1ENR; // wait 1 APB1 cycle (dummy read) for change being valid TIM14->CR1=TIM_CR1_URS_Msk; // default settings, counter stopped TIM14->DIER=0; // disable all timer interrupts TIM14->CCMR1=0; // capture/compare not used TIM14->CCER=0; // capture/compare not used TIM14->SR=0; // clear all interrupt flags NVIC_EnableIRQ(TIM14_IRQn); // enable NVIC interrupt from timer // start TIM14->PSC=div1-1; // set the prescaler [1-65536] for APBx clock TIM14->ARR=div2; // set the counter top limit (when it overflows) TIM14->EGR=TIM_EGR_UG_Msk; // force update event to reload PSC and cleat CNT TIM14->SR=0; // clear all interrupt flags TIM14->DIER=TIM_DIER_UIE_Msk; // enable update event interrupt TIM14->CR1|=TIM_CR1_CEN_Msk; // start the counter counting up from 0 to ARR while (timer->DIER&TIM_DIER_UIE_Msk); // wait until interrupt is not disabled by ISR timer->CR1&=~TIM_CR1_CEN_Msk; // stop the counter // ISR void TIM14_IRQHandler(void) { if (timer->SR&TIM_SR_UIF_Msk) // if the interrupt was caused by update evt. timer->DIER=0; // disable all timer interrupts timer->SR=0; // clear all interrupt flags }
- Generování určité frekvence na pinu pomocí časovače: pro tento účel využijeme jednu output compare jednotku (kanál) časovače, nelze tedy použít časovače ze skupiny Basic Timers. Ostatní skupiny mají 1 - 4 kanály input capture / output compare, které lze kombinovat podle potřeby. Základní princip je ten, že učitý kanál s vyhrazeným registrem propojíme s určitým pinem MCU, v případě režimu input capture (měření časových úseků) se při definované události na pinu přepíše okamžitá hodnota čítače do vyhrazeného registru a případně vyvolá přerušení, v režimu output compare (generování frekvence) se naopak do vyhrazeného registru předem zapíše určitá hodnota, při jejímž dosažení čítačem se vyvolá určitá akce na vybraném pinu (nastavení 0 / 1 nebo inverze předchozího stavu). Je možné dokonce nakonfigurovat 2 komplementární piny, které budou inverzně měnit stav včetně vložení programovatelného deadtime. Toho lze využít třeba pro řízení můstku pro motorek nebo spínaný zdroj. Následující kód generuje frekvenci se střídou 50% na jednom pinu pomocí časovače TIM2 a OC kanálu 2.
SET_PORT_MODE(GPIOC, 5, GPIO_MODE_ALT, GPIO_OTYPE_PP, GPIO_FLOAT, GPIO_SPEED_HIGH); SET_PORT_AF(GPIOC, 5, AF2); // configure GPIO pin to alt. func. (timer OC) TIM2->PSC=div1-1; // set the prescaler [1-65536] for APBx clock TIM2->ARR=div2-1; // set the counter top limit (when it overflows) TIM2->EGR=TIM_EGR_UG_Msk; // force update event to reload PSC and cleat CNT TIM2->SR=0; // clear all interrupt flags timer->CCR2=timer->ARR>>1; // set the chan. 2 duty cycle to 50% (OCMP match value) // OC2M3:0=0111b (PWM2 output mode), CC2S1:0=0 (channel 2 is out), set preload enable timer->CCMR1=(0x7<<TIM_CCMR1_OC2M_Pos)|TIM_CCMR1_OC2PE_Msk; // for channel 2 timer->CCER=TIM_CCER_CC2E_Msk|TIM_CCER_CC2P_Msk; // enable chan. 2 OC, polarity=active low TIM2->CR1|=TIM_CR1_CEN_Msk; // start the counter counting up from 0 to ARR
- Využití DMA přenosu: STM32G0 popis DMA na STM32F4