FULL-C: tips and tricks
Tato stránka popisuje programovací jazyk FULL-C, který je dostupný na vybraných zařízeních SDS. Některá zařízení používají SDS-C, pro který máme návody jinde na této WiKi.
Důležité poznámky k práci s FULL-C
Zde jsou neseřazené tipy a triky, tak, vytvořené tak, jak jak postupem času dostáváme různé dotazy od uživatelů.
Současně (na konci stránky) naleznete kritické tipy a postupy, jak efektivně zapsat váš program tak, aby byl co nejvíce rychlý. Je důležité si to přečíst.
- Dbejte na to, aby verze FC byla stejná jak pro překladač (FULLC.EXE) tak i pro vaše SDS. Pokud má vaše SDS starší firmware (a tedy starší verzi FC, například 0x06), zatímco překladač je novější (například FULLC.exe s verzí FC 0x08), tak SDS odmítne novější FC program spustit. Toto se řeší velmi snadno, a to aktualizací firmware ve vašem SDS.
- I když proběhne překlad ve FULLC.EXE v pořádku, stále může váš FULL-C program obsahovat chyby - to se dozvíte teprve z konzole (web zařízení SDS: typicky 192.168.1.250/echo.htm), proto ji vždy průběžně kontrolujte !
- Pozor na signed a unsigned typy a jejich používání. FULL-C se chová dle standardu C, a detaily standardu vás mohou zmást.
Např. char je signed ! (pokud jej explicitně nedefinujeme jako unsigned char).
Příklad - chceme vypsat HEX kód pro jednobajtový znak.
Toto ale neudělá to co "chceme": vypíše to FFFFFFDB - přesně podle specifikace jazyka C, kdy dojde nejprve k převodu na int32
char ch = 0xDB;
printf("%x", ch);
Příklad správného zápisu: vypíše to DB tak jak chceme, a to proto že jsme správně uvedli že to chceme zpracovat jako unsigned char.
char ch = 0xDB;
printf("%x", (unsigned char)ch);
Další příklad velkého problému je následující kus programu pro výpis obsahu textového pole jako HEX znaků:
char pole[32];
char * ch;
ch = (char *)&pole;
for (i = 0; i < 32; i++)
{
printf("%02x", *ch++); // <<<<<<< WARNING !!!! *ch is signed char !!!
}Opět, správné řešení je ve správném přetypování:
printf("%02x", (unsigned char)*ch++);
- Před provedením jakékoliv síťové komunikace ve FULL-C (např. odeslání emailu), vždy zkontrolujte stav, a to pomocí: status = SDS_get_u(27); (když je status==0 tak SDS nemá připojení - nezačínejte komunikaci, čekejte a testuje stav).
- Pole pro uchování textů můžete přímo deklarovat, např. na zásobníku (stack) jako char text[128]; - nebo na haldě (heap), pro což využijte text = (char *)malloc(128); a free(text);text=0; funkce. Výběr z těchto možností je na vás (malé pole / malé texty jdou dát na zásobník, velké už patří do haldy).
- Využijte 64-bitových proměnných (int64_t nebo uint64_t) tam, kde vám 32 bitů nestačí. Například pro počítadla, nebo výpočty s velkými čísly (nebo jen stačí že by měl být velký mezivýsledek a při použití 32-bit čísel by jste o něj "přišli").
- Pro zjištění, jaký největší blok paměti (heap) lze v danou chvíli alokovat pomocí malloc() funkce, využijte funkci SDS_heap_stats() . Ačkoliv může být celková hodnota volné heap pamět velká, toto celkové volné místo je vždy roztroušeno na různé malé oddělené bloky (fragmentace) - a vždy lze alokovat jen po jednom bloku, a jeho největší možná velikost je zjistitelná právě ze zmíněné funkce.
- Vždy když uvolníte paměť, nezapomeňte ihned ručně vynulovat daný uvolněný ukazatel. Zabrání to chybám ve vašem programu. Příklad:
// definice ukazatale
void *ptr;
...
// alokujeme si pamet, pro priklad 32 bajtu
ptr = malloc(32);
// pamet na kterou ukazuje ptr lze ted pouzivat (pozor at nepristupujete mimo alokovany rozsah)
...
// uz pamet nepotrebujeme, tak ji uvolnime:
free(ptr);
ptr = 0; // vzdy ihned za zavolanim free() nastavte ptr do nuly !!!
...
- Funkce printf() a puts() a putchar() zapisují do konzole (web zařízení SDS, typicky: 192.168.1.250/echo.htm), čehož lze využít pro ladění programu, nebo pro dálkový monitoring zařízení. Nicméně pro průběžné spolehlivé získávání hodnot a údajů z SDS nebo vašeho programu, je vhodně použít systém sdílených proměnných, nebo SNMP.
- FULL-C používá právě přesně 32-bitů pro datový typ float. Počítejte s touto informací pro správné pochopení přesnosti práce s desetinnými čísly (omezené rozlišení dané 32bity). Pro většinu aplikací toto nepředstavuje problém, pokud ale chcete provádět složité výpočty s desetinnými čísly, je potřeba znát tato omezení. Přesné vysvětlení viz např. "Single-precision floating-point format".
- Funkce printf() a sprintf() umí zpracovat float proměnné (%f), ale neumí je uživatelský formátovat (neumí např. %0.2f - ale umí jen základní %f, to znamená že vypíše úplně celou délku čísla se všemi pozicemi za tečkou, což může být i dost dlouhý text). Toto je aktualizováno v nové verzi FULL-C (tam už to jde).
- Nepoužívejte prázdný string (tj. "").
Tři příklady zakázaného kódu pro FULL-C:
// do not do any of the following:
//
char text1[10] = ""; // spatne !
//
strncpy(text2,"",10); // spatne !
//
sprintf(text2,""); // spatne !
- Před použitím některé z funkcí, které přistupují k serveru na Internetu, je vhodné provést volání dns_resolv() pro zjištění aktuální IP adresy (IP0.IP1.IP2.IP3) daného serveru, vždy před zavoláním konkrétní funkce (např. http_get() ). Určitě není vhodné používat přímo natvrdo zapsané číselné adresy (ty se mohou změnit např. při přesunu serveru).
- Pokud některá z FULL-C funkcí selže (např. smtp_send() ohlásí přes smtp_send_status() chybu), tak je vhodné mít FULL-C program napsán tak, aby chvíli počkal, a pak to celé zkusil znovu. Při více selháních je pak potřeba provést další akci (oznámit chybu, zjistit stav připojení k síti, atd.).
- Přes FULL-C lze pouze číst kalibrační konstanty pro analogové vstupy. Pokud je chcete hromadně nastavit, použijte s výhodou 192.168.1.250/cgi_cfg_adc?xxxx volání (uživatel musí být přihlášen).
- Každý FULL-C program může nastavit tzv. "brand" (uživatelskou značku / popisek) pro zařízení SDS, na kterém je spuštěn. Protože SDS je často dodáváno v rámci OEM pro další zástavbu do většího celku, může si finální systémový integrátor nastavit své doplňující texty (název firmy, odkazy na web, telefonní čísla na podporu, doplňující texty, informace o sériovém čísle atd.), které jsou pak zobrazeny v rámci stránek webové administrace zařízení SDS (login, welcome, atd.). Toto nastavení provádí FULL-C program zavolání SDS_set_a() s příslušným indexem a textem. Typicke se takový FULL-C kód dává na úplný začátek programu.
- FULL-C program lze nachystat a vydat tak, že je "zablokován" tak aby fungoval pouze na jediném konkrétním vybraném SDS zařízení. Každé SDS má své unikátní sériové číslo (viz tabulka indexů pro SDSget_u() ), a FULL-C program může tuto hodnotu porovnat (s tím, co do FULL-C programu vloží autor programu). Toto uživatelské zablokování programu může předejít chybám, kdy by se např. nevhodný program nahrál omylem na jiný hardware. Pokud vydáváte FULL-C program prostřednictvím .FC souboru, kam zvolíte nepřiložit zdrojový .C kód, pak máte neprůstřelnou možnost zamezit použití programu na jakémkoliv jiném SDS než jaké sami určíte (zdrojový kód nebude cílovému uživateli k dispozici, a navíc váš program otestuje sériové číslo daného SDS na shodu). Samozřejmě to znamená, že musíte zmíněné sériové číslo daného SDS zjistit předem, než vydáte program, aby jste to číslo mohli do programu vložit.
- Pokud ve svém programu využíváte sériovou sběrnici RS485, dbejte na správné zapojení sběrnice. Naše fórum je plné příkladů, kdy autor programu řešil problém s RS485 a nakonec se ukázalo, že sběrnice byla špatně zapojená (přehozené A/B dráty, nezakončená rezistory, neuvedená do známého klidového stavu přes rezistory atd.). Pro ověření programu je vždy dobré vzít pasivní "odposlech" sběrnice (osciloskop, osobní počítač) a zkontrolovat "překryv" (kdy více zařízení vysílá najednou, což je na RS485 kolize a poškození dat). Právě "překryv" je nejčastějším problémem způsobující poškození komunikace, hned po špatném nastavení komunikační rychlosti v programu. Vždy zkontrolujte svůj hardware, tj. celou sběrnici, je-li správně impedančně zakončena (nejvzdálenější konce) a zda-li jste správně zapojili oba klidové rezistory (pozor, na SDS-BIG tyto nejsou a musí se ručně doplnit "zvenku").
- Pokud chcete přes sdílené proměnné (sv) přenášet čísla datového typu float (desetinná čísla), a pak je chcete například zobrazit na UserWEB webové stránce (a nebo přenést na server atd.), použijte následující trik. Vytiskněte obsah float čísla do Txx[] proměnné, pomocí sprintf(), nikoiv však s využitím %f - ale právě přes následující kód:
// definujeme si sdilenou promennou T00
char T00[32];
void FloatToT00(void)
{
// vstupni float
float xf;
// pomocna promenna (presne 4 bajty, protoze "float" je ve FULL-C reprezentovan jako 32-bit)
unsigned int xw;
// zapiseme vzorovou hodnotu
xf = 12.34;
// ziskej 32bit obsah xf (= cteni 4 bajtu dat ktere reprezentuji dany float)
xw = *(unsigned int *)&xf;
// a zapis do T00, zarovnej vzdy na osm mist (at se to da pozdeji spravne zpracovat - prevest zpet na float)
sprintf((char *)T00, "%08X", xw);
// T00[] ted obsahuje "414570A4" (coz je hex reprezentace puvodni float hodnoty 12.34)
}
Pokud se po spuštění tohoto kódu pak podíváte např. na 192.168.1.250/shared.txt , tak tam uvidíte toto: "T00|414570A4|" (krom jiného textu). S tím už pak v JavaScriptu není problém pracovat (převést zpět na float a korektně zobrazit nebo dále zpracovat). Řada přímých příkladu je k dispozici, stačí hledat na Internetu podle klíčových slov "javascript convert hex to float". Toto se řeší právě z důvodu že FULL-C printf funkce (a další podobné) umí jen %f ale bez dalších parametrů, takže tisknou float čísla na všechny místa za desetinnou tečkou, což je ve většině případů nežádoucí (při zobrazování na webových stránkách a podobně).
- Přistupovat ke všem údajům ze sběrnic 1-Wire, ve FULL-C, je samozřejmě možné, je však potřeba vědět jak. Detaily viz stránka FULL-C: OneWire.
- Pamatujte, že varianta SDS-64 (viz varianty) má k diposozi pouze celkem 64kB programové paměti (a SDS-128 má jen 128kB této paměti, a SDS-512 jen 512kB celkové FULL-C paměti) - do které se musí vlézt celý váš přeložený FULL-C program, a k tomu také celý STACK a všechna potřebná paměť HEAP. Toto omezení není dáno programově, ale právě použitým hardware v SDS, takže jej nelze jednoduše "obejít" - pokud se vám to nevleze do varianty 64 tak musíte použít variantu 128 (nebo 512), to je jediné řešení.
- Vždy správně nastavte velikost STACKu - číselnou hodnotu nastavte v okně editoru zdrojového textu FULL-C. Pokud nastavíte hodnotu pro váš program jako moc malou, program nebude fungovat (při volání více funkcí může být pro nedostatek stacku ukončen). Příliš velká hodnota snižuje volnou paměť heap (pokud heap nepoužíváte, nastavte stack na vyšší hodnotu pro klid). Správnou hodnotu zjistíte určitým odhadem (podle velikosti a složitosti programu, podle toho kolik proměnných používáte a kolik parametrů se předává při volání funkcí, a kolik funkcí se v sobě volá). Nastavení je možné nechat automaticky odhadnou překladač, nebo jej můžete ručně zadat (odhad není přesný).
- Inicializaci proměnných provádějte v kódu, nikoliv při deklaraci. Jde o to, že při inicializaci hodnot v proměnné či poli, provedené kódem, se spotřebuje zlomek stack paměti, oproti inicializace přímo při deklaraci. Stack paměti je vždy málo a je potřeba jí šetřit. Je to vlastnost FULL-C.
Příklad inicializace při deklaraci:
unsigned int numbers[5] = {0,1,2,3,4}; // toto spotřebuje hodně stacku - TAKHLE TEDY NE
Příklad identické inicializace kódem:
// toto řešení je doporučené, nespotřebovává stack
unsigned int numbers[5];
number[0] = 0;
number[1] = 1;
number[2] = 2;
number[3] = 3;
number[4] = 4;
Nebo, příklad odpovídající inicializace kódem:
// toto řešení je doporučené, nespotřebovává stack
unsigned int numbers[5];
unsigned int i;
for (i = 0; i < 5; i++) { number[0] = i; };
>>>>>>>>>>>> Následující informace je platná pouze pro firmware starší než 10.7.2017
- SDS ukazuje aktuální využití své 64kB programové paměti, na webové stránce v administraci.
HEAP STATS: curalloc=1892, totfree=58632 maxfree=58632, nget=12 nrel=0 HEAP STATS: bc_locked=1120, stackTop=2984 (of 5000)
Jak to dékódovat: první řádek ukazuje celkové využití paměti a počet alokací a uvolnění:
curalloc = celkem alokovaná paměť (heap malloc, a všechny interní FULL-C funkce a interní data v SDS) totfree = celkem zbývající volná (alokovatelná) heap pamět (součet všech volných heap fragmentů) maxfree = největší volný heap fragment, který je možné alokovat v jednom kuse nget = celkový počet alokací (volání malloc/calloc) (včetně vnitřních volání interních FULL-C funkcí v SDS) nrel = celkový počet uvolnění předchozích alokací (volání free() funkce)
Druhý řádek se vztahuje na FULL-C program samotný a jeho běh:
bc_locked = kolik bajtů paměti je využito (uzamčeno) pro 'bytecode' vašeho FULL-C (kolik si vzal samotný kód FULL-C programu v paměti) stackTop = kolik bajtů zásobníku (stacku) je v tento okamžik využito (pokud je to na limitu, víte, že musíte upravit nastavení při překladu) (of X) = kolik bajtů může nejvíce zásobník (stack) zabrat z celé paměti (nastaveno uživatelem před překladem)
>>>>>>>>>>>> Následující informace je platná pouze pro firmware 10.7.2017 (včetně) a novější (viz také varianty)
- SDS ukazuje aktuální využití své 64kB nebo 128kB nebo 512kB programové paměti, na webové stránce v administraci.
FC: VER 0x08, PROGMEM 128kB MEM STATS: - HEAP: used=(492 of 120675), totfree=120164 (biggest=65396), num(get=11,rel=0) - STACK: used=(364 of 10004) - BC: locked=(264+128+388) - SYS: free=280
Jak to dékodovat: první řádek ukazuje informace o SDS:
VER = verze FC PROGMEM = varianta hardware (64kB nebo 128kB nebo 512kB celkové paměti pro program a data)
Druhý řádek ukazuje celkové využití paměti a počet alokací a uvolnění:
HEAP used = celkem alokovaná paměť (heap malloc, a všechny interní FULL-C funkce a interní data v SDS) totfree = celkem zbývající volná (alokovatelná) heap pamět (součet všech volných heap fragmentů) biggest = největší volný heap fragment, který je možné alokovat v jednom kuse nget = celkový počet alokací (volání malloc/calloc) (včetně vnitřních volání interních FULL-C funkcí v SDS) nrel = celkový počet uvolnění předchozích alokací (volání free() funkce)
Druhý řádek se vztahuje na FULL-C program samotný a jeho běh:
STACK used = kolik bajtů zásobníku (stacku) je v tento okamžik využito (pokud je to na limitu, víte, že musíte upravit nastavení při překladu)
Třetí řádek se vztahuje na FULL-C program samotný a jeho běh:
BC locked = kolik bajtů paměti je využito (uzamčeno) pro 'bytecode' vašeho FULL-C (kolik si vzal samotný kód FULL-C programu v paměti)
Čtvrtý řádek nemá pro FULL-C význam:
SYS = interní hodnota
Jak funguje FULL-C v SDS
Zde jsou popsány hlubší technologické detaily popisující princip FULL-C. Zajímat budou zejména ty, kteří pracují na složitých programech.
Program je uživatelem zadán jako jeden jediný textový soubor - zdrojový kód v jazyce FULL-C. Tento zdrojový kód (text) je zpracován v naší Windows aplikaci (FULLC.exe, k dispozici ke stažení na této WiKi) do formy výsledného souboru, který primárně obsahuje tzv. bytecode. Tento soubor je následně přes síť nahrán do konkrétního zařízení SDS, kde je trvale uložen (až do nahrání jiného souboru) a je zařízením SDS spouštěn.
Komprese
Program je přenášen z FULLC.exe do SDS přes počítačovou síť. Aby se nemuselo přenášet tolik dat, je bytecode komprimován. Typická úspora je 25% původního objemu.
S touto informací je potřeba plánovat při navrhování rozložení paměti - aby se tam vše vešlo. To že komprimovaný program (výstup FULLC.exe) se do paměti SDS na první pohled "vleze", ještě neznamená, že se program i podaří spustit (stále se může "vlézt", ale nezbude místo na zásobník nebo haldu - a to je konec).
ZÁKLADNÍ OMEZENÍ:
Zařízení SDS je k dispozici ve třech základních variantách: varianta "64" má k dispozici 64kB paměti pro celkové potřeby programu, a varianta "128" má k dispozici 2x64kB (tj. 128kB) paměti pro celkové potřeby programu. Varianta "512" nabízí největší dostupnou paměť pro program a data.
Do celé této celkové paměti se musí vlézt nejen surový bytecode ale i všechny provozní záležitostí (zásobník - stack, proměnné, halda - heap).
Skladba paměti - varianta "64":
(START I-64kB BLOKU)
0 + zásobník (stack)
...
+ řetězce programu (string blob)
...
+ instrukce programu (bytecode)
...
+ nevyužitelná rezerva systému (zhruba 260 bajtů)
...
+ proměnné programu (variables)
...
+ halda (heap)
...
64kB + konec
(KONEC I-64kB BLOKU)
Další paměť (více než 1x 64kB) už není ve variantě "64" k dispozici - vše se tedy musí vlézt do jednoho 64kB velkého bloku. Pokud to pro vaše potřeby nestačí, musíte použít variantu s pamětí 128kB nebo 512kB, které jsou popsány dále.
Skladba paměti - varianta "128":
(START I-64kB BLOKU)
0 + zásobník (stack)
...
+ řetězce programu (string blob)
...
+ nevyužitelná rezerva systému (zhruba 260 bajtů)
...
+ proměnné programu (variables)
...
+ první část haldy (heap-1)
...
64kB + konec
(KONEC I-64kB BLOKU)
(START II-64kB BLOKU)
0 + instrukce programu (bytecode)
...
+ druhá část haldy (heap-2), jen pokud nezabere program celý II-64kB blok
...
64kB + konec
(KONEC II-64kB BLOKU)
Jak lze vidět, varianta "128" nabízí dva bloky po 64kB. Tyto bloky na sebe sice lineárně nenavazují (tj. součtem je to sice 128kB, ale ne v celku), nicméně přináší významnou úlevu předchozímu omezení původní varianty "64".
Pro použití varianty "128" je ovšem potřeba mít správný hardware (SDS), tzn. SDS výrobní varianty "64" nelze programově změnit na variantu "128" - to lze jedině použitím vhodného SDS hardware. Samozřejmě program původně určený pro "64" lze na SDS "128" spustit, bez jakýchkoliv omezení (ale naopak, program 128 nelze na 64 spustit, protože varianta 64 nemá dost paměti pro program varianty 128).
Skladba paměti - varianta "512":
(START I-504kB BLOKU)
0 + zásobník (stack)
...
+ řetězce programu (string blob)
...
+ nevyužitelná rezerva systému (zhruba 260 bajtů)
...
+ proměnné programu (variables)
...
+ první část haldy (heap-1)
...
504kB + konec
(KONEC I-504kB BLOKU)
(START II-8kB BLOKU)
0 + druhá část haldy (heap-2)
...
8kB + konec
(KONEC II-8kB BLOKU)
SOUČET: celková dostupná paměť 504+8 = 512kB
Jak lze vidět, varianta "512" nabízí dva bloky, jeden 504kB a druhý 8kB. V součtu je k dispozici 512kB paměti, která je (dle rozpisu uvedeného výše) rozdělena na jednotlivé dynamické části - skutečně použité velikosti každé části už pak závisí na obsahu vašeho FULL-C programu a vašem nastavení (vámi určená velikost zásobníku).
Programy určené pro variantu 512 lze spustit pouze na správném typu hardware - SDS varianty 512.
Další informace:
Velikost programu (bytecode + stringblob) a proměnných (variables) je pevná, a je přímo dána vámi tj. programátorem (podle toho, jaký napíšete program, tak bude tato oblast velká).
Zbytek je použit pro haldu (heap), což je různě veliký paměťový prostor který má program k dispozici (funkce: malloc, realloc, free), pokud jej tedy potřebuje. Pokud bude váš program moc velký, pak se může stát že halda nemusí existovat - a systém nebude schopen takový program vůbec spustit ! Vždy je potřeba mít haldo o velikosti alespoň hodnoty 1/8 velikosti programu.
Zásobník (stack) je vždy umístěn na začátku paměťového prostoru, a jeho velikost je opět jednorázově určena programátorem (podle odhadu nebo aktuální měření). Pokud je zásobník příliš malý, může být program předčasně ukončen (řešením je zvětšení velikosti zásobníku (na úkor velikosti heapu) a nové nahraní programu do zařízení). Toto je zřejmě nejčastější problém u většiny FULL-C programů - špatně nastavená velikost zásobníku.
Informace o aktuálním vyžití celého paměťového prostoru je k dispozici ve webovém rozhraní SDS (Menu, FULL-C). Odsud lze také přečíst skutečné využití zásobníku (nejvyšší dosažená velikost).
Heap
Typický FULL-C program v zařízení SDS nemá potřebu používat haldu (heap) - typicky všechny proměnné (i velká pole) lze bez problému staticky deklarovat.
Pokud však potřebujete dynamickou alokaci paměti, tak je k dispozici ten zbytek paměťového prostoru, který zůstane "volný" po nahrání programu (a vyhrazení zásobníku).
Informace o velikosti volného heapu a fragmentaci lze opět nalézt ve webovém rozhraní SDS.
Opakování varování
Velikost programu je omezena takto:
- varianta SDS 64kB: vše se musí vlézt do 64kB (bytecode, proměnné, zásobník)
- varianta SDS 128kB: vše se musí vlézt do 2x 64kB (128kB s danými omezeními, především bytecode omezen na 64kB protože se musí celý vlézt do jednoho bloku)
- varianta SDS 512kB: vše se musí vlézt do 512kB, přičemž součet velikosti (bytecode, všechny proměnné, zásobník) je do velikosti 504kB (vše další je dostupné jako heap)
- Zásobník (stack) musí mít dostatečnou velikost pro provoz programu (všechny vnořené volání funkcí, rekurze, atd.).
- Halda (heap) musí být k dispozici o dostatečné velikosti, jinak se jakýkoliv program nespustí.
Pokud by váš přeložený program byl moc velký, a to tak že se spolu s ním do paměti nevlezou další potřebné věci (zásobník atd.), nebudete moct program na zařízení SDS spustit.
Vždy můžete kontrolovat stav programu (a využití paměti) na stránce "FULL-C" ve webové administraci zařízení SDS.
Jak programovat
FULL-C program musí vždy mít definovanou funkci main(), která je první spuštěnou funkcí v rámci spuštění programu.
void main(void)
{
;
}
Je úkolem programátora, aby funkce main() nebyla opuštěna (potom je program "ukončen" protože nemá kam dál pokračovat) = v takovém případě je program ukončen, a znovu bude spuštěn až po restartu SDS (nebo když by byl nahrán nový program, tak je pak spuštěn ten).
FULL-C program tedy vždy začíná jako první provádět instrukce ve funkci main().
V základu lze činnosti provádět na základě těchto vstupů:
- čas (např. každou vteřinu)
- hardware (např. čtení vstupů)
- hodnoty sdílených proměnných (např. změnou proměnné z uživatelské webové stránky)
Výstupy programu mohou být:
- ovládání hardware (např. spínání relé)
- změna hodnot sdílených proměnných (např. pro dynamické zobrazení na uživatelské webové stránce)
- odesílání stavů přes síť (např. HTTP-GET nebo SNMP-TRAP)
Pro všechny tyto vypsané činnosti je potřeba znát funkce a příkazy, které programu umožní právě takovéto činnosti vykonávat.
OPTIMALIZACE KÓDU a jak je SDS vůbec rychlé ?
Bylo provedeno detailní měření na SDS-BIG 128 a SDS-BIG 512, a postupy jsou použitelné pro všechna BIG a BIG2 zařízení.
Pokud není uvedeno jinak, měřilo se při nezatížené síti. U SDS-BIG 64 lze očekávat prakticky stejné hodnoty jako u SDS-BIG 128, protože má stejnou frekvenci CPU.
Časy uvedené v textu platí pro SDS-BIG 64/128. SDS-BIG 512 je téměř ve všem cca 3,5x rychlejší (viz tabulka). Jedinou výjimkou jsou makra s parametry. Všechny tipy platí pro obě varianty stejně.
Základní orientace
- Jeden jednoduchý příkaz (např.
x = y + z;) trvá cca 60–150 µs, tj. řádově 10 000 jednoduchých příkazů za sekundu. Hlavní smyčka se 100 příkazy tak trvá cca 10 ms na jeden průchod. - Samotný výpočet je zanedbatelný:
+ - * / %, 64bitová i float aritmetika trvají stejně. O rychlosti rozhoduje počet příkazů a způsob zápisu programu (viz tipy níže). - Výsledky měření jsou velmi opakovatelné (±0,1 %). Při nezatížené síti spotřebuje ostatní činnost SDS jen cca 2 % výkonu.
Naměřené časy
Platí pro SDS-BIG, BIG2 a všechny varianty (SMALL atd.) odvozené od BIG a BIG2.
| Zápis | SDS-BIG 64/128 | SDS-BIG 512 |
|---|---|---|
prázdný cyklus for (i = 0; i < n; i++) { }, jeden průchod |
109 µs | 31 µs |
for (i = n; i--; ) { } / while (i--) { }, jeden průchod |
52 / 46 µs | 15 / 13 µs |
x = 5; (x je lokální proměnná) |
cca 60 µs | cca 17 µs |
x = y + z; |
cca 105 µs | cca 31 µs |
x++; / ++x; / x += 1; / x = x + 1; |
57 / 49 / 70 / 102 µs | 17 / 14 / 20 / 30 µs |
konstanta: číslo / enum / #define (definované těsně před použitím) |
+0 / +7 / +21 µs | +0 / +2 / +6 µs |
konstanta: enum / #define (definované na začátku velkého programu) |
+47 / +62 µs | +15 / +19 µs |
| globální proměnná, čtení / zápis (deklarovaná na začátku programu), oproti lokální | +32 / +90 µs | +11 / +31 µs |
| globální proměnná, čtení / zápis (deklarovaná těsně před použitím), oproti lokální | +7 / +13 µs | +2 / +4 µs |
makro s parametry, např. SQR(y) |
870–1600 µs (!) | cca 3550 µs (!) |
totéž jako funkce sqr(y) / rozepsané y * y |
cca 195 / 105 µs | cca 60 / 31 µs |
| volání prázdné funkce bez parametrů | cca 105 µs | cca 36 µs |
| každý parametr funkce / každá lokální proměnná ve volané funkci, na každé volání | +35 / +65 µs | +10 / +18 µs |
příkaz, který se neprovede (uvnitř nesplněné podmínky if) |
cca 80 µs | cca 25 µs |
| volání funkce, které se neprovede (bez ohledu na velikost funkce) | cca 20 µs | cca 6 µs |
switch nebo řetězec if – else if, na každou větev (bez ohledu na to, která platí) |
cca 80–100 µs | cca 25–35 µs |
tabulka hodnot x = tab[k]; |
cca 120 µs | cca 35 µs |
SDS_get_u32() / SDS_set_u32() |
cca 155 / 250 µs | cca 48 / 79 µs |
8 vstupů: 8x SDS_get_u32() / 1x SDS_get_a() |
1400 / 320 µs | 416 / 98 µs |
memcpy, strcpy, strcmp, strlen, snprintf, atoi, ... (jedno volání) |
200–340 µs | 60–110 µs |
kopie 64 bajtů vlastním cyklem / pomocí memcpy |
11–18 ms / 0,34 ms | 3,1–5,2 ms / 0,11 ms |
printf() |
cca 360 µs + 2,7 µs na znak | cca 110 µs + 1,1 µs na znak |
putchar(), na každý znak |
cca 375 µs | cca 115 µs |
wait(0) / wait(N) |
0,6 ms / N ms + 0,5–0,7 ms | 0,19 ms / N ms + 0,1–0,15 ms |
echoclear() |
cca 1 ms | cca 0,5 ms |
Proměnné a konstanty
- Nejčastěji používané proměnné funkce (čítače cyklů, mezisoučty) deklarujte jako poslední v seznamu deklarací. Proměnná deklarovaná později je rychlejší: čítač cyklu deklarovaný jako první z 27 lokálních proměnných zpomalí každý průchod cyklem o 38 µs, deklarovaný jako poslední nezpomalí nic.
- Konstanty, globální proměnné a enum deklarujte co nejblíže místu použití, např. těsně před funkcí, která je používá, nebo těsně před
main()pro hlavní smyčku. Totéž definované na začátku velkého programu je výrazně pomalejší (viz tabulka). Navíc je to i čitelnější: definice je hned u použití. - Méně globálních položek = rychlejší program. Každá další globální proměnná, funkce nebo konstanta mírně zpomalí přístup ke všemu, co je v programu deklarováno před ní, včetně volání funkcí SDS.
- Související globální proměnné spojte do jedné struktury (
struct). Přístup k položce struktury stojí jen cca +14 µs. - Nevytvářejte zbytečně mnoho drobných globálních pomocných funkcí.
- Související globální proměnné spojte do jedné struktury (
- Používejte lokální proměnné. Na začátku funkce si zkopírujte globální proměnné a nastavení do lokálních proměnných, v cyklu pracujte s lokálními a výsledek zapište do globální proměnné jen jednou na konci.
- Pro pojmenované konstanty používejte
enummísto#define.#defineje o cca 14 µs pomalejší než enum na stejném místě v programu.#definepoužijte jen tam, kde ho FULL-C vyžaduje (velikost pole). - Nepoužívejte makra s parametry (
#define SQR(a) ((a)*(a))) v často prováděném kódu.- Stojí 870–1600 µs na každé použití a jejich rychlost se mění s velikostí programu a stavem paměti.
- Na SDS-BIG 512 jsou dokonce pomalejší než na SDS-BIG 128 (cca 3550 µs).
- Použijte funkci (cca 195 µs, na SDS-BIG 512 cca 60 µs) nebo výraz rozepište.
- Nastavení, které uživatel mění, může zůstat na začátku programu jako
#definenebo globální proměnná. Často prováděný kód si ale tyto hodnoty na začátku funkce zkopíruje do lokálních proměnných. - Nedeklarujte proměnné uvnitř těla cyklu (
for (...) { int t; ... }). Funguje to, ale deklarace se provádí při každém průchodu (+25 µs, na SDS-BIG 512 +8 µs). Deklarujte na začátku funkce.
Cykly
- Cyklus směrem dolů je dvakrát rychlejší:
for (i = n; i--; ) { ... }trvá 52 µs na průchod,for (i = 0; i < n; i++)109 µs. V těle cyklu má i hodnoty n-1 ... 0.while (i--)je stejně rychlý. while (i < n) { ...; i++; }anido { } while ()nejsou rychlejší nežfor.++ije o něco rychlejší neži++,i += 1je pomalejší.- Nikdy nepoužívejte globální proměnnou jako čítač cyklu (+113 µs na každý průchod).
- Mez cyklu jako číslo nebo lokální proměnná nic nestojí. Jako globální proměnná stojí +29 µs na průchod, jako
#define+56 µs na průchod. Zkopírujte ji předem do lokální proměnné. - Nejvíc ušetříte tím, co z cyklu vyjmete: každý příkaz uvnitř cyklu stojí cca 100 µs na každý průchod.
Podmínky a kód, který se neprovede
- I kód, který se neprovede, zabírá čas. Příkaz uvnitř nesplněné podmínky stojí cca 80 µs, skoro stejně jako provedený (cca 100 µs).
- Velké, zřídka prováděné bloky přesuňte do samostatné funkce. Neprovedené volání funkce stojí jen cca 20 µs, bez ohledu na to, jak je funkce velká.
returnčas neušetří. Volání funkce trvá úměrně délce celé funkce, ne jen té části, která se provedla. Často volané funkce udržujte krátké.switchi řetězecif – else ifstojí cca 80–100 µs na každou větev, bez ohledu na to, která větev platí: 2 větve cca 270 µs, 8 větví cca 770–940 µs.- Kde to jde, použijte tabulku hodnot místo dlouhého
switchnebo řetězceif(x = tab[k];cca 120 µs). Tabulku naplňte kódem na začátku programu, protože FULL-C nepovoluje inicializaci pole v deklaraci. &&pravou stranu sice neprovede, ale i tak stojí čas.- Ternární operátor
?:trvá stejně jakoif / elsea vždy vyhodnotí obě strany. Používejte ho jen s čísly: s řetězci (ok ? "OK" : "CHYBA") se program zastaví s chybou "first argument to '?' should be a number".
Výrazy a příkazy
- Méně příkazů je rychlejší: jeden výraz
x = ((y+z)*(y-z))/3;trvá 233 µs, totéž rozdělené do 4 příkazů 393 µs. - Používejte zkrácené zápisy:
x++57 µs,x += 170 µs,x = x + 1102 µs. - Bitový posun a násobení / dělení trvají stejně,
* 8není třeba nahrazovat zápisem<< 3.
Datové typy
int64_t,uint64_tanifloatnejsou pomalejší nežint, používejte je bez obav.charanishortnejsou rychlejší nežint.ftoi()je pomalejší než přetypování(int)(199 µs proti 146 µs).
Funkce
- Funkce jsou dost rychlé na to, abyste mohli psát přehledný kód: volání stojí cca 100 µs, tedy zhruba jako jeden příkaz. Rozdělte program do funkcí podle úkolů.
- Každý parametr prodlouží volání o cca 35 µs, každá lokální proměnná deklarovaná ve volané funkci o cca 65 µs, každá další úroveň vnoření volání o cca 90 µs.
- Malé, často volané funkce by měly pracovat jen se svými parametry, bez vlastních lokálních proměnných.
- Pro předání většího množství dat předejte ukazatel na strukturu místo mnoha parametrů.
- Vyhněte se řetězcům drobných obalových funkcí a rekurzi (stojí čas i místo v zásobníku).
- Velikost zásobníku (STACK) nastavená ve FULLC.exe musí pokrýt nejhlubší vnoření volání.
Řetězce a paměť
- Vždy používejte knihovní funkce (
memcpy,memset,strcpy,strlen,strcmp,snprintf,atoi,strtoul, ...). Jedno volání stojí 200–340 µs bez ohledu na délku dat. Totéž vlastním cyklem je 20–50x pomalejší: kopie 64 bajtů v cyklu trvá 11–18 ms. snprintfvčetně formátu%0.2ffunguje a stojí cca 260–340 µs.- Nepoužívejte
malloc/freev cyklech. Fragmentace haldy (heap) zpomaluje program a může způsobit selhání síťových funkcí. Paměť alokujte jednou na začátku programu, nebo použijte pevná pole. Nechte dostatek volné haldy pro síťové funkce.
Funkce SDS (set a get)
SDS_get_*()stojí cca 155 µs,SDS_set_u32()cca 250 µs.- Kde to tabulka indexů umožňuje (označení "(a)"), čtěte bloky pomocí
SDS_get_a(): 8 vstupů jedním voláním za 320 µs místo 1400 µs. Pro optovstupy jen na firmware, kde je opravena známá chyba 5 (viz níže). - Hodnotu přečtěte jednou za průchod do lokální proměnné a dál používejte tu.
SDS_get_a()vyplní jen tolik položek, kolik jich daný hardware má. Požadujete-li víc, funkce nevyplní nic a nehlásí chybu. Proto vždy žádejte jen skutečný počet položek. Např. SDS-BIG 512 (BIG2): 9 optovstupů, 6 relé, 8 A/D vstupů, 9 S0 vstupů.
Konzole (printf)
printf()stojí cca 360 µs + 2,7 µs na znak.putchar()stojí cca 375 µs na každý znak, proto nikdy netiskněte po znacích.- Celý řádek si připravte pomocí
snprintf()a vytiskněte ho jedním voláním. - Plná konzole tisk nezablokuje ani nezpomalí, ale její kapacita je omezená (BIG: pod cca 14 kB výstupu). Tiskněte stručně.
- Konzole (echo.htm) je HTML stránka: znak
<, za kterým následuje písmeno nebo číslice, se bere jako HTML značka a část textu se nezobrazí. Ve výpisech se znaku<vyhněte.
wait() a měření času
- SDS-BIG 64/128:
wait(N)trvá N ms + cca 0,5–0,7 ms,wait(0)0,6 ms. Při silném zatížení sítě můžewait(N)trvat až o cca 1,7 ms déle. - SDS-BIG 512:
wait(N)trvá N ms + cca 0,1–0,15 ms,wait(0)0,19 ms. wait(0)nepoužívejte jen pro "uvolnění" procesoru.- Pro měření času použijte index 45 (uptime v ms).
- Pro přesná měření lze použít index 137 (čítač cyklů CPU). Na SDS-BIG 64/128 má 168 kroků na 1 µs a přeteče cca po 25 s, na SDS-BIG 512 má 480 kroků na 1 µs a přeteče cca po 9 s.
Sériová linka (UART)
Měřeno na COM6 s propojeným TX a RX, při rychlostech 9600–460800 Bd. Ve všech testech, i při silném zatížení sítě, proběhl přenos bez jediné chyby. RS485 na COM1 nebyla měřena.
| SDS-BIG 64/128 | SDS-BIG 512 | |
|---|---|---|
SDS_serial_writeCH(), na každý bajt |
cca 0,5 ms | cca 0,16 ms |
SDS_serial_write(), jedno volání |
cca 0,85 ms + 1,6 µs na bajt | cca 0,37 ms + 0,6 µs na bajt |
vysílací buffer (SDS_serial_write() se vrací ihned) |
cca 600 B | nejméně 1024 B |
| přijímací buffer | nejméně 1024 B | nejméně 1024 B |
SDS_serial_read(), když žádná data nečekají |
cca 0,35 ms | cca 0,11 ms |
| nejkratší dotaz – odpověď (zápis až přečtení), kromě doby přenosu po lince | cca 4 ms (pod zátěží sítě až 6 ms) | cca 1,2 ms (pod zátěží sítě až 2,6 ms) |
| data lze přečíst po příchodu posledního bajtu za | cca 2,5 ms | cca 0,7–0,8 ms |
- Data odesílejte jedním voláním
SDS_serial_write(), nikdy po znacích pomocíSDS_serial_writeCH()v cyklu: 64 bajtů po znacích trvá 33–40 ms na SDS-BIG 128 a 10 ms na SDS-BIG 512, při jakékoliv rychlosti linky. SDS_serial_write()se vrací ihned, aniž čeká na odvysílání, pokud se data vejdou do vysílacího bufferu. Při větším objemu čeká, dokud se zbytek do bufferu nevejde: na SDS-BIG 128 čekalo 1024 B při 115200 Bd 34 ms.- Přijímací buffer pojme nejméně 1024 B, takže program nemusí číst nepřetržitě.
SDS_serial_read()nevolejte v rychlé smyčce, protože stojí čas i bez dat. Stačí jednou za průchod hlavní smyčkou nebo powait().- Přijatá data jsou programu k dispozici se zpožděním (viz tabulka). U krátkých zpráv při vysokých rychlostech toto pevné zpoždění převažuje, takže vyšší rychlost linky pomůže hlavně u velkých bloků dat.
MQTT
- Nevolejte další funkci
mqtt_*(), dokud předchozí není dokončena: u publish, subscribe a unsubscribe sledujte[12202], u connect a disconnect[12200]. Ve FW 0x0019080B takové volání poruší stav spojení (viz Známé chyby níže). - Stav spojení
[12200]kontrolujte při každém průchodu hlavní smyčkou a v případě potřeby spojení obnovte postupem uvedeným v části Známé chyby. Po každém novém připojení je potřeba znovu provést subscribe, protože se tabulka přihlášení maže. - Po normálním
mqtt_disconnect()skončí[12200]na hodnotě −2, ne 0. Považujte 0 i −2 za "odpojeno". - Kontrola stavu v čekací smyčce stojí čas (cca 0,3 ms na SDS-BIG 128). Do takové smyčky dejte
wait(10).
| SDS-BIG 128, FW starší než 10.2026 | SDS-BIG 128, FW od 31.10.2026 včetně | SDS-BIG 512, FW od 31.10.2026 včetně | |
|---|---|---|---|
mqtt_connect() |
cca 0,2–0,45 s | cca 0,45 s | cca 2 ms |
| publish (do potvrzení brokerem) | cca 0,3–0,65 s (průměr 0,5 s) | cca 3–6 ms | cca 0,9 ms |
| nejvyšší počet publish za sekundu | cca 2 | cca 250 | cca 1000 |
| zpráva přes broker a zpět | cca 0,5 s | cca 6 ms | cca 1,7 ms |
| subscribe / unsubscribe / disconnect | cca 0,3–0,65 s | cca 3–4 ms | cca 1 ms |
| při silném zatížení sítě | beze změny (cca 0,5 s) | většina publish 7–15 ms, některé až cca 1,5 s | publish cca 1,3–1,9 ms, výjimečně až cca 50 ms, cca 600 zpráv za sekundu |
- Pro FW starší než 10/2026: délka operace nezávisí na velikosti dat, a protože každá operace trvá cca 0,5 s:
- na dokončení publish v hlavní smyčce nečekejte. Zavolejte
mqtt_publish(), pokračujte v jiné práci a[12202]zkontrolujte při dalším průchodu; - posílejte více hodnot v jedné zprávě (např. JSON, celkem do cca 1465 znaků včetně názvu položky): publish s 1024 B trvá stejně dlouho jako s 8 B.
- na dokončení publish v hlavní smyčce nečekejte. Zavolejte
- Firmware od 31.10.2026 včetně: čekat na dokončení publish lze, jedna operace trvá jednotky ms. Vždy ale používejte časový limit alespoň 3–5 s, protože při zatížení sítě může operace výjimečně trvat i cca 1,5 s.
Známé chyby firmware (MQTT)
1. Volání mqtt_*() dříve, než skončila předchozí operace, poruší stav spojení (FW 0x0019080B, ve FW od <doplnit> opraveno)
- Zavoláte-li např.
mqtt_publish()ve chvíli, kdy předchozí publish ještě probíhá, zapíše se chybový kód −3 do[12200](místo do[12202]).[12200]pak zůstane trvale na −3, přestože spojení ve skutečnosti funguje, a program se tak jeví jako odpojený. - Následné přímé zavolání
mqtt_connect()selže s výsledkem −7 (při CONNACK 0). - Ochrana: nikdy nevolejte další funkci
mqtt_*()před dokončením předchozí (viz MQTT výše). - Obnova spojení, která funguje ve všech verzích firmware:
- pokud
[12200]není 4, 0 ani −2, zavolejtemqtt_disconnect(); - počkejte, až bude
[12200]rovno 0 nebo −2 (cca 0,3 s); - počkejte dalších 500 ms;
- zavolejte
mqtt_connect()a počkejte na hodnotu 4; - znovu proveďte subscribe všech položek.
- pokud
2. Po mqtt_disconnect() skončí [12200] na −2 místo 0 (všechny testované verze firmware)
- Hodnota −2 v tomto případě neznamená chybu: považujte 0 i −2 za "odpojeno". Příklad, který po odpojení čeká pomocí
if (sys[2200] <= 0) goto error;, by normální odpojení vyhodnotil jako chybu.
Zatížení sítě a rezerva výkonu
- SDS-BIG 64/128:
- Při nezatížené síti spotřebuje ostatní činnost SDS cca 2 % výkonu a program není nikdy přerušen na déle než cca 0,6 ms.
- Při silném zatížení (ping a HTTP dotazy z jednoho PC) spotřebuje 15–19 % výkonu. Program pak běží o cca 18–20 % pomaleji a je často přerušován, ale nikdy na déle než cca 1,5 ms.
- SDS-BIG 512:
- Při nezatížené síti spotřebuje ostatní činnost SDS cca 1 % výkonu a program není přerušen na déle než cca 0,03 ms.
- Při silném zatížení (ping a HTTP dotazy z jednoho PC) spotřebuje ostatní činnost cca 45 % výkonu a program běží cca 1,8x pomaleji. I tak je ale stále cca 2x rychlejší než SDS-BIG 128 bez zátěže.
- Občas, pravděpodobně při otevření stránky webového rozhraní přes HTTPS, se objeví jednotlivá pauza cca 10–25 ms. Programy s krátkými časovými limity (např. sériové protokoly) musí takovou pauzu snést.
- U zařízení se silným síťovým provozem počítejte s rezervou výkonu: cca 25 % na SDS-BIG 64/128, cca 50 % na SDS-BIG 512. Poměry mezi časy i všechny tipy výše platí i pod zátěží.
Sběrnice 1-Wire
- Pokud sběrnici 1-Wire nepoužíváte, zavolejte hned na začátku programu
SDS_disable_onewire(). - Na SDS-BIG 512 s firmwarem starším než 31.10.2026 může odpojené čidlo 1-Wire program extrémně zpomalit (určitě si aktualizujte firmware!).
- Stav 1-Wire masteru lze zkontrolovat čtením indexu 2998: 1 = detekce, 2 nebo 4 = chyba, 8 = v provozu, 16 = vypnuto.
Známé chyby ve starším firmware (starší než 31.10.2026)
1. Odpojené čidlo 1-Wire zpomalí program cca 125x (opraveno ve FW od 31.10.2026 včetně)
- Pokud je v SDS evidováno dříve nalezené čidlo 1-Wire, které je fyzicky odpojené, zpomalí se FULL-C program cca 125x. Zpomalení začne, jakmile se 1-Wire master rozběhne (cca 7 s po startu SDS, index 2998 = 8), a program se už nezotaví.
- Program pak dostane méně než 1 % výkonu: smyčka, která běžně trvá 54 µs na průchod, trvá 6,6–9,3 ms.
- Ochrana: pokud 1-Wire nepoužíváte, zavolejte na začátku programu
SDS_disable_onewire(). S ním běží program trvale plnou rychlostí. - Ochrana pro starší firmware, pokud 1-Wire používáte: zajistěte, aby všechna evidovaná čidla byla připojena, nebo aktualizujte firmware.
2. Blokové čtení optovstupů SDS_get_a(151, ...) vrací chybné hodnoty (opraveno ve FW od 31.10.2026 včetně)
- SDS-BIG 512: volání může způsobit restart celého SDS.
- <doplnit po ověření na SDS-BIG 64/128: vrací nesmyslné hodnoty místo stavu vstupů>
- Ve FW od <doplnit> opraveno: blokové čtení vrací stejné hodnoty jako
SDS_get_u32(151 + n)(0 = aktivní, 1 = neaktivní). - Ochrana pro starší firmware: čtěte optovstupy jednotlivě pomocí
SDS_get_u32(151 + n), nebo aktualizujte firmware.
Co rychlost neovlivní (pište přehledně)
- Komentáře, i uvnitř cyklů a v neprováděných blocích.
- Dlouhé, výstižné názvy proměnných.
- Závorky navíc.
- Rozdělení programu do funkcí podle úkolů (cca jeden příkaz na každé volání).
- 64bitové a float typy.
- Odsazení, prázdné řádky, formátování.
Příklad
Pomalý, ale běžný zápis:
#define OPTO_FIRST 151
#define NUM_INPUTS 8
#define WEIGHT(a) ((a)*(a))
unsigned int g_weight;
void main(void)
{
unsigned int i;
for (;;)
{
g_weight = 0;
for (i = 0; i < NUM_INPUTS; i++)
{
if (SDS_get_u32(OPTO_FIRST + i) == 0) g_weight = g_weight + WEIGHT(i + 1);
}
wait(100);
}
}
Totéž zapsané podle tipů výše (stejně čitelné):
#define NUM_INPUTS 8 // velikost pole: zde je #define nutny
unsigned int g_weight; // vysledek pro zbytek programu
enum inputs { OPTO_FIRST = 151 }; // definovano tesne pred pouzitim
void main(void)
{
unsigned int opto[NUM_INPUTS];
unsigned int w;
unsigned int i; // nejpouzivanejsi promenna deklarovana posledni
for (;;)
{
SDS_get_a(OPTO_FIRST, (void *)opto, NUM_INPUTS * 4); // 1 volani misto 8
w = 0;
for (i = NUM_INPUTS; i--; ) // cyklus dolu: i = 7 .. 0
{
if (opto[i] == 0) w += (i + 1) * (i + 1); // bez makra s parametry
}
g_weight = w; // globalni promenna zapsana jen jednou
wait(100);
}
}
Změny oproti první verzi:
- jedno blokové čtení místo 8 volání;
- žádné makro s parametry;
- mezisoučet v lokální proměnné místo globální;
- cyklus směrem dolů;
- konstanta definovaná těsně před použitím;
- čítač cyklu deklarovaný jako poslední.
Podle naměřených časů trvá jeden průchod první verze na SDS-BIG 128 cca 8–15 ms (podle počtu aktivních vstupů), druhé verze cca 2–3 ms. Na SDS-BIG 512 je to cca 3,5x méně u obou verzí; u první verze je rozdíl ještě větší, protože makra s parametry jsou na SDS-BIG 512 pomalejší.