Debugovací přípravek pro ATtiny
Když dělám nějaké prográmky pro ATtiny, stává se mi, že občas potřebuji při ladění zobrazit nějaký mezivýsledek výpočtu, nebo nějkou změřenou hodnotu. Problém je v tom, že Attiny má omezenou paměť, takže knihovny pro sériovou komunikaci nebo dokonce I2C komunikaci se tam už obvykle nevejdou. Omezení bývá i v počtu volných I/O pinů. Většinou jsem to řešil nějakým blikáním jedné LED (na internetu jsem našel třeba kus kódu, který postupně "vybliká" i několikaciferné číslo). Takovéto kódy už jsou ale zase docela náročné na paměť ATtiny. Navíc existuje značné riziko, že se při ručním počítání bliknutí spletu. Je to i pomalé (vyblikání jednoho trojciferného čísla může trvat třeba i víc než 20 sekund). Napadlo mě, že bych místo blikání použil jen rozsvícení LED na nějakou dobu, která by odpovídala číslu. Například číslo 0 by bylo signalizováno impulzem o délce 50us, číslo 1 impulzem 100us, 2...150us , a tak dále až k číslu 255, které by přenesl impulz délky 12,8ms. Bylo by samozřejmě nutné pro měření délky impulzu použít nějaké přesné stopky (druhé Arduino). Vysílání by bylo jednoduché a zabralo by jen pár bajtů v paměti ATtiny:
Kód pro příjem v druhém Arduinu by byl taky jednoduchý. Předpokládal jsem, že použiju funkci pulseIn(), která je přímo stavěná na měření délky impulzů. Získané číslo by se pak někde zobrazilo (sériový terminál, displej ...)
Jenže takhle jednoduché
to není!
První důvod, proč to nefunguje, je v tom, že ATtiny nemá přesný vnitřní generátor, takže šířka impulzu nemusí úplně přesně odpovídat požadovanému času. Tahle chyba se projevuje více na delších intervalech. Tento problém jsem v prvním kroku vyřešil kalibračním impulzem, který se odesílá před každým číslem. Kalibrační impulz byl dlouhý třeba 1ms (z pohledu vysílacího ATtiny) a podle toho, jak dlouhý impulz ve skutečnosti změřilo přijímací Arduino se pak druhý impulz, který už přenášel číslo, korigoval. Větší problém ale byl v tom, že když jsem zkusil generovat v ATtiny řadu stejně dlouhých 1ms impulzů, tak byl každý jinak dlouhý. Odchylka mezi minimální a maximální naměřenou hodnotou byla třeba i přes půl procenta. Tato nepřesnost způsobovala, že se výsledné číslo mohlo lišit od vysílaného čísla až o +/- 1. Při dlouhodobém testování (několik hodin) se občas objevila odchylka, která byla ještě větší. Chyba by pak občas mohla být i +/- 2 čísla. V dalším kroku jsem tedy přidal do kalibračního impulzu ještě informaci, která jednoznačně upřesní hodnotu odesílaného čísla. U odesílaného čísla zjistím, jaký je jeho zbytek po dělení 5. (zbytek = cislo % 5) Podle výsledku se nastaví délka kalibračního impulzu: Pokud je odesílané číslo přesně dělitelné 5, je kalibrační impulz dlouhý 5ms. Pokud je zbytek po dělení 1, je kalibrační impulz dlouhý 6ms. Pokud je zbytek po dělení 2, je kalibrační impulz dlouhý 7ms. Pokud je zbytek po dělení 3, je kalibrační impulz dlouhý 8ms. Pokud je zbytek po dělení 4, je kalibrační impulz dlouhý 9ms. Takto velké rozdíly není problém v přijímacím Arduinu rozlišit. Když pak zbytek po dělení vypočteného čísla odpovídá délce kalibračního pulzu, předpokládám, že bylo měření v pořádku. Když délka kalibračního pulzu neodpovídá zbytku po dělení vypočteného čísla, je provedena celočíselná korekce výsledného čísla na takovou hodnotu, aby délka kalibračního impulzu souhlasila. Příklad: Má se odeslat číslo 103. Zbytek po dělení pěti je tedy 3. ATtiny odešle první impulz, který by měl být dlouhý 8ms. Přijímací Arduino tento impulz přijme a změří, že ve skutečnosti trvá 7.93ms. Tím je téměř s jistotou dáno, že zbytek po dělení výsledného čísla musí být 3. (I když ten impulz netrvá přesně 8ms, je jasné, že k 8ms má výrazně blíž, než k 7ms.) Druhý impulz, který vyšle ATtiny by měl být dlouhý ((103 + 1) * 50us) = 5200us. Protože přijímací Arduino nemá seřízený krystal na úplně stejnou frekvenci, jako má vnitřní oscilátor ATtiny, změří Arduino délku druhého impulzu třeba jen 5097us. Na základě prvního kalibračního pulzu, který měl být 8ms, ale byl jen 7.93ms se určí, že skutečná jednotková délka času je 7.93ms / 160 = 49.5625us. (160 jednotek by mělo teoreticky trvat 8ms) Změřená délka druhého impulzu se vydělí touto jednotkovou konstantou, zaokrouhlí se a převede se na celé číslo: int( (5097 / 49.5625) - 0.5) = 102 Číslo 102 má ale zbytek po dělení pěti roven 2. To neodpovídá požadovanému zbytku po dělení, který by měl být 3. Proto se pomocí korekční tabulky najde takové nejbližší číslo ke 102, pro které platí ten požadovaný zbytek po dělení pěti. V tomto případě je tedy nejbližší nižší číslo 98 (to by ale znamenalo korekci -4). Nejbližší vyšší číslo je 103 (to je na korekci +1). 103 je tedy nejblíž a je to správný výsledek. Příklady skutečných signálů: detail jednoho přenášeného čísla přenos dvou čísel (130 a 213) Schéma: verze pro Eagle: tiny_debug.sch Programy: Vysílací část pro ATtiny se skládá ze 3 částí, které se přidají k původnímu programu, který se nachází v ATtiny. 1) V globální části je nadefinovaný vysílací pin. Volí se odkomentováním jedné ze 6 řádek. (V případě, že se má pro odesílání čisel použít resetovací pin (PB5), je nutné změnit v ATtiny FUSE.) 2) Samostatný podprogram, který převádí číslo na dva impulzy. 3) V sekci setup() je třeba přenastavit vysílací pin na výstup Požadované číslo se pak do displeje odesílá v libovolné části původního programu pomocí volání podprogramu: debug(číslo); Kromě běžných jednobajtových čísel (0 až 255) je možné odeslat i několik řídících kódů, které upravují zobrazení na displeji:
Přepínač na přípravku umožňuje přepsat nastavení režimu zobrazení. V poloze "Tiny" respektuje režim zobrazení, který odesílá Attiny. V polohách "DEC", "HEX", "CHAR" a "BIN" ale zobrazuje všechna přijatá čísla ve zvoleném režimu - nezávisle na tom, jaký režim chce zobrazovat ATtiny.
Ukázkový příklad do ATtiny s odesíláním čísel do různých poloh displeje s různými režimy zobrazení: Program pro přijímač (Arduino NANO):
|