čtvrtek 31. března 2016

Brutální optimalizace kódu za použití logického analyzátoru

Znáte to, objednáváte na Aukru nějakou haléřovou položku a skončíte s vrchovatým košíkem za jeden a půl tyčky... jen proto, že je to za jedno poštovné. A to se vyplatí!

No, a takovým nějakým způsobem se mi ocitl na stole USB Logic Analyzer 24M 8Ch.

A zcela nečekaně, dá se říci náhodou, mi pomohl dokončit projektík, který už jsem prakticky odpískal. Ale popořádku...


Kolize + Expozice

Rozpor nebo konflikt, který vyvolává dramatické napětí a potřebu jej vyřešit. Zcela proti zákonům dramatu vložíme Exozici přímo do Kolize.

Na úvodu jsem zmínil projektík, který jsem málem odpískal kvůli překážkám, které jsem nedokázal překonat. Oč se tedy jedná? Chtěl jsem si vyrobit kuchyňskou minutku, moji, vlastní, přesně dle mých přání. Chtěl jsem aby:
  1. Měla maximálně jednoduché ovládání
  2. Měla svítící display (byl vidět v šeru)
  3. Byla mnou opravitelná
  4. Byla pokud možno mechanicky odolná
Bod jedna je důležitý. Uvědomte si, jak používáte digitální minutku. Třeba když nastavujete čas pro vylouhování Earl Gray (4 minuty):
  • Stisknete současně tlačítka MIN+SEC pro reset nastavených hodnot (už se vám někdy stalo, že nastavený čas vám vyhovoval?)
  • Stisknete 4x po sobě MIN
  • Stisknete START/STOP
U klasické minutky jen otočíte kroutítkem na vybraný čas a už to jede. Takže Moje minutka musí mít kroutítko. Odborně se mu říká rotační enkodér.

Expozice - Rotační enkodér (rotary encoder)

Expozice nás uvádí do děje a vytváří nezbytné předpoklady k jeho porozumění.

První vzorky jsem vypreparoval z rozbitých myší, kde se s nimi realizuje to lupkací kolečko mezi levým a pravým tlačítkem. Nový kus stojí tak do 60,- a některé umí reagovat i na stisk tlačítka.

Pro úplnost je třeba dodat, že existují také další provedení, kde jsou mechanické spínače nahrazeny optickou závorou a clonkou. K vidění jsou třeba u starých modelů myší, těch s kuličkou. 

Rotary encoder

Při otáčení se na výstupech A a B generují tyto signály:

Signály na výstupech enkodéru.
Posloupnost těchto signálů nás informuje o směru otáčení. V praxi reagujeme zpravidla na náběžnou hranu signálu A a v tu chvíli si přečteme hodnotu na B. Pokud je log0, točí se enkodér na jednu, pokud log1, tak na druhou stranu.

Stejný princip se používá i v odometrii (roboty, CNC stroje, ...).

Expozice - Logický analyzátor (LA)

Jest takový udělátor, které jedním koncem připojíte do USB vašeho počítače, dalším drát připíchnete na zem zkoumaného obvodu a zbývající vývody zapojíte tam, kde vás zajímá, co se zrovna děje. Potom nastavíte pár parametrů, kliknete na START a rázem máte jako na dlani všechny děje, které se odehrály na sledovaných vývodech.

Jeden z možných výsledků snímání.
Software Saleae Logic 1.2.5 Beta

Můj LA vyrobili soudruzi v Číně. Má osm kanálů, tj. dokáže sledovat změny logických úrovní na osmi místech najednou. Sice se jedná se o sprostou kopii Saleae Logic, ale rozdíl v ceně je tak výrazný, že jsem si jej mohl pořídit. Jak je to s kvalitou nedokážu posoudit, zatím funguje. Software je potřeba použít z originálu (https://www.saleae.com/).

8 kanálový LA od Číňana (klon)
Cena: 200 - 400,-

16 kanálový LA ze San Francisca
Cena (pro osm kanálů): 5000,-

Stručně řečeno: čím jsou osciloskopy v analogové elektrotechnice, tím jsou logické analyzátory v technice číslicové.

Krize

Expozici a kolizi máme za sebou, tak co teď asi může přijít...

I nastrkal jsem do breadboardu nezbytné součástky, vše propojil, přivedl napájecí napětí a jal se programovat.

Připojen je i LA, ale ten jsem v té době ještě neměl.

Ale ouha, výsledky posílané přes sériový výstup byly... hmmm... zmatené. Usoudil jsem, že problém bude asi v kvalitě ´mnou produkovaného kódu, takže jsem zapátral po internetu a vyzkoušel asi deset dalších programů... výsledek byl stejný. Dal jsem další enkodér, opět chyby, koupil jsem nový enkodér, opět to samé.

Že by byl problém v čase, který sériový výstup potřebuje k odeslání dat. No co, knihovnu pro LED zobrazovadlo s čipem TM1637 jsem už měl z jiného projektu hotovou, tak jsem posílal čísla rovnou tam. Stejný (špatný) výsledek.

Došly nápady, takže deska se zapojením ležela na stole a při pohledu na ni se mi chtělo zvracet. To jsem vám neříkal, že mám občas při programování takové stavy?

Peripetie

tedy obrat a možnosti řešení.

Uběhlo pár dní, zabýval jsem se skvělým mikrokontrolérem ESP-8266 (provedení 07) a do nákupu nějakých chybějících součástek jsem přibalil i LA. V podstatě jsem jej pořídil ze zvědavosti a z nezkrotné touhy se něco nového naučit, teď jsem ho měl, ale co k němu připojit? Klasik by napsal: "Můj zrak padl na rotační enkodér". Ve skutečnosti mě to trklo v nekonečné frontě u nefungujících závor.

Po chvilce laborování a mudrování se zapojením a programem, chápejte, bylo to poprvé, jsem si ověřil, že enkodéry skutečně generují správný signál (viz. obrázek výše). Super, tak aspoň něco.

Z LA ale čučelo ještě šest nevyužitých drátů, kam je tak připojit? Dva jsem dal do CLK a DIO u zobrazovače, mrknu se co tam posílám. Třetí (o něco později) jsem připojil k vývodu piezoměniče.

Opět chvíle laborování, spuštění LA, otočení kolíkem enkodéru, zobrazení dat na monitoru... a málem mi spadla čelist. Já debil, já kretén, já...! Vždyť je to nad slunce jasnější, jak jsem mohl být tak zabedněný, tupý, atd, atp. Posuďte sami:

Výstup LA pro první, zcela neoptimalizovanou, verzi programu.
V signálu 01 (Enc master) jsem červeně zvýraznil interval mezi dvěmi náběžnými hranami enkodéru (přeskok jednoho zoubku v enkodéru). Odhadem je to 20 ms, netočil jsem enkodérem moc rychle, během kterých je potřeba něco vypočítat a poslat data do zobrazovače.

Takže je zcela jasné, že než obsluha zobrazování vybaví, třetí byte, tak přijde další náběžná hrana (kterou nezaregistrujeme a nezpracujeme) a v polovičce zpracování pátého bytu přijde ještě jedna náběžná hrana (kterou opět nezaregistrujeme a nezpracujeme). A tak vznikne chaos...

V1. Neoptimalizovaná verze

Prostě na odeslání dat potřebujeme 40 ms, ale máme jen 20 ms.

Poznámka

Odesílaný balík dat má délku 5 bytů (5x8 bitů + nějaké servisní bity), což je těch pět shluků v signálu 03 (CLK). Samotná data jsou v 02 (DIO), ale těmi se teď nebudeme zabývat. Na začátku procedury, která zpracovává přerušení od enkodéru, jsem lupnul piezoměničem (high b.0 : low b.0), to je těch 45.42 ms v 04 (interrupt). Posílá se adresa prvního znaku (1. byte) následovaná postupně definicí tvaru znaku 1 až 4 (každý po 1 bytu).

Délka jednoho CLK je 0.24 ms = 240 µs

Katarze

též rozuzlení a očista.

Teď víme, čím to je, takže co s tím?

V2. Posíláme jen to nejnutnější - 17 ms

Změnou logiky programu teď aktualizuji jen pozice, které se změnily. To znamená 2 byty (adresa + tvar znaku). Trochu to pomohlo, ale ne moc. Enkodér totiž může posunovat čas po 10 sec, což v některých případech emituje nutnost aktualizovat až 3 číslice najednou, to je až 6 bytů, pokud bych silně nezkomplikoval logiku vykreslování, pak by to byly 4 byty.

Výsledek je tady, nic moc, zrovna se sešlo souběžné zobrazení více číslic. Jedna číslice (víc vpravo) je už ok.

Výstup LA pro druhou, lehce optimalizovanou verzi programu,
kdy jsou zobrazovači zasílány jen změny konkrétních pozic.

V3. Mizí elegance programu, ale stoupá rychlost - 13 ms

Zkusil jsem optimalizovat tu část programu, která v cyklu rozkládá 1 byte na bity a posílá je do zobrazovače.

Původní verze:

SendData:
   for bitIndex=1 to 8 'odeslání bytu po jednotlivých bitech
      low clk

      if bit0=1 then 
         high dio 
      else 
         low dio 
      endif 'polohování DIO

      ledData = ledData/2 'Posun vlevo
   next

   'Potvrdíme příjem (ACK)
   low clk

   do:loop while pinDio=1

   high clk
   low clk
return

Optimalizovaná verze:

SendData:
   low clk: if bit0=1 then high dio : else low dio : endif : high clk
   low clk: if bit1=1 then high dio : else low dio : endif : high clk
   low clk: if bit2=1 then high dio : else low dio : endif : high clk
   low clk: if bit3=1 then high dio : else low dio : endif : high clk
   low clk: if bit4=1 then high dio : else low dio : endif : high clk
   low clk: if bit5=1 then high dio : else low dio : endif : high clk
   low clk: if bit6=1 then high dio : else low dio : endif : high clk
   low clk: if bit7=1 then high dio : else low dio : endif : high clk

   'Potvrdíme příjem (ACK)
   low clk

   do:loop while pinDio=1

   high clk
   low clk
return

Výsledek dává tušit, že cyklus a matematické operace měly dost velkou režii.

Výstup LA pro druhou verzi optimalizace.

3. Lopatou do držky, ale zato rychle - 5 ms

Tady už je zbytečné rozepisovat jednotlivé kroky optimalizace, takže jen to hlavní:

Definice tvaru znaku se již nedekóduje ze zápisu:

'zobrazujeme desítky sekund
ShowSec10:
   charIndex = sec10 - 48
   gosub DecTo7seg
   gosub CSendData
return

DecTo7seg:
   '                  0   1   2   3   4   5   6   7   8   9
   lookup charIndex,($3f,$06,$5b,$4f,$66,$6d,$7d,$07,$7f,$6f), ledData 'číslice 0-9
return

ale rovnou se volá procedura pro zaslání konkrétního znaku. V následujícím příkladě se ještě:
  • úrovně DIO nastavují pouze, je-li to nutné
  • zapulzování CLK děje příkazem PULSOUT (dříve sled příkazů HIGH clk a LOW clk)
  • doplnila konstanta CLK_TIME s kterou je možné dále experimentovat

'zobrazujeme desítky sekund
ShowSec10:
   charIndex = sec10 - 48

   low clk

   on charIndex gosub SendDigit0,SendDigit1, ... ,SendDigit9

   gosub Ack
return

'00111111
SendDigit0:
   high dio : pulsout clk, CLK_TIME
              pulsout clk, CLK_TIME
              pulsout clk, CLK_TIME
              pulsout clk, CLK_TIME

              pulsout clk, CLK_TIME
              pulsout clk, CLK_TIME
   low  dio : pulsout clk, CLK_TIME
              pulsout clk, CLK_TIME
return


ve stejném duchu byly změněny procedury pro adresaci, nastavení jasu atd.

Výsledkem je skvělý čas 5 ms, dohromady s režií 9 ms.

Výstup LA pro třetí, finální, verzi optimalizace.

Délka jednoho CLK je 8 µs
Poznámka: při optimalizaci se mi začali ztrácet impulzy v kanále 03 (CLK). Na jeden byte jich má být osm, občas tam byli jen dva. Zařízení však fungovalo. Nebudu vás napínat, bylo to způsobeno nízkou frekvencí vzorkování (Sample Rate), kdy děj vznikl i zanikl dřív, než jej LA stihnul zaregistrovat. Po přenastavení Sample Rate z hodnoty 50kS/s na 500kS/s bylo již vše v pořádku.

Závěr

Pochopil jsem, že pokud programujeme mikrokontroléry, musíte zatvrdit své srdce, rezignovat na krásu a eleganci kódu a zaměřit se jen na funkci.







Žádné komentáře:

Okomentovat