Tak po takřka dvou letech vcelku bezproblémového provozu se nám poprvé na tiskárně něco vážněji porouchalo. V sobotu jsme s Kryštofem zkoušeli tisknout jednoduchou helikoptéru na gumu, když tu přestal fungovat posun vlákna v extruderu. Krokový motor se přestal otáčet. Ostatní osy chodily. Zkoušel jsem zda jsou v pořádku kabely a konektory tím, že jsem přepnul extruder na jiný pololu driver a zkusil s ním pootočit. To fungovalo, takže krokový motor a jeho kabel není ten problém. Jak jsem tak prohazoval kabílky ke krokovým motorům všiml jsem si, že když se dotknu 5V regulátoru, krokové motory se občas divně cuknou. Pak se chvíli zdálo, že se tiskárna umoudřila. Ale za chvíli se symptomy vrátily a ještě výrazně zhoršily, a posléze přestaly komunikovat všechny krokové motory. Tiskárna se občas přihlásila k PC, a občas se jevila jako úplně mrtvá.
Zkoušel jsem nejdřív přeletovat 5V regulátor (říkal jsem si, že tam může být špatný kontakt), posléze jsem ho pro jistotu zcela vyměnil za nový LM7805. Tiskárna se nyní regulerně hlásí k PC a komunikuje s ním (takže dost možná prapříčinou problémů přece jen mohl být vadný regulátor). Je možné nastavit a stabilizovat teplotu jak pro extruder, tak pro podložku. Když se však pokouším pohnout krokovými motory chová se to velmi zvláštně: Příslušný motor se sice dá do pohybu, nicméně rychlostí asi tak jeden mikrokrok za sekundu, a pokračuje ve svém pohybu donekonečna. Zkoušel jsem vyměnit pololu/stepstik destičky za jiné, které shodou okolností mám v šuplíku - žádná změna.
Takže se zdá, že v řídícím mikrokontroleru Atmel ATmega644 nějakým způsobem běží firmware (procesor tedy není docela mrtvý), ale nekomunikuje dobře se stepstick/pololu drivery. Může to být, že se z nějakého důvodu poškodil částečně hardware Atmelu, takže program uvnitř běží, ale vstupní/výstupní porty nemají správné napěťové úrovně. Nedá se ani vyloučit, že se část firmware (např. obsah eeprom??) poškodila. Problém může být i s dalším hardare na desce Sanguinololu.
Takže plán útoku je následující:
1) Zkusím přeprogramovat firmware ve stávajícím procesoru
2) Pokud to nepomůže koupím nový ATMega644, nahraju do něj bootloader a firmware.
3) Pokud nepomůže ani to, koupím celou novou desku Sanguinololu
Samozřejmě jsem při té příležitosti zjistil, že nemám zálohovanou aktuální verzi firmware, která nám v tuto chvíli tiskárnu řídí. Totiž, v počátečních stádiích vývoje tiskárny jsem měl veškerý software na jedné fleshce, protože jsem používal k ovládání tiskárny netbook mojí manželky a nechtěl jí tam nechávat software na pevném disku. Když jsem pak ovládací software migroval na Ubuntu linux, byl již firmware nahraný a tak jsem aktuální verzi Arduino zdrojových kódů na tento počítač nezkopíroval. No, a fleshka se za ty dva roky někam zatoulala.
Trik je v tom, že ten firmware byl poněkud modifikovaná verze oproti stažené varianě Sprinteru. Jednak jsem ji modifikoval, aby bylo možné firmware nahrát na ATMega644 a navíc jsem přídával podporu pro thermistor s pozitivní teplotní závislostí (KTY84). Ty první modifikace jsem měl ještě jakž takž popsané v poznámkách o stavbě tiskárny (viz jeden z předchozích blogů), modifikaci pro thermistor však už ne tak docela (Mám tam vtipně poznamenáno, že to není tak triviální jak jsem si původně myslel, a že si snad udělám poznámky později. Neudělal...).
Takže jsem se musel znovu ponořit do trošku nepřehledného zdrojového kódu Sprinteru a provést odpovídající změny: Naštěstí se snad ukazuje, že přece jenom co člověk již jednou dělal v paměti částečně uvízne, takže to nebylo tak bolestné, jako poprvé. I když jistotu, že jsem to udělal správně ještě nemám, to budu vědět až teprve když firmware úspěšně nahraji a tiskárna opět poběží.
Ale abych podruhé neudělal úplně tu samou chybu, pokusím se alespoň nahrubo dokumentovat postup:
Pro doprogramování podpory pro KTY84 bylo potřeba provést změny v následujících souborech:
Configuration.h
heater.cpp
thermistortables.h
V souboru Configuration.h:
pouze přidáme do poznámky u nastavení termistorů, že varianta 8 odpovídá KTY84 NTP termistoru:
//// Thermistor settings:
// 1 is 100k thermistor
// 2 is 200k thermistor
// 3 is mendel-parts thermistor
// 4 is 10k thermistor
// 5 is ParCan supplied 104GT-2 100K
// 6 is EPCOS 100k
// 7 is 100k Honeywell thermistor 135-104LAG-J01
// 8 is KTY 84 positive slope thermistor. Added O.V. 2014
#define THERMISTORHEATER 8 //extruder uses KTY84 thermistor
#define THERMISTORBED 4
V heater.cpp:
Je potřeba upravit rutiny ve kterých se čte analovová hodnota z termistoru:
pro extruder:
#ifdef HEATER_USES_THERMISTOR
current_raw = analogRead(TEMP_0_PIN);
#ifdef DEBUG_HEAT_MGMT
log_int("_HEAT_MGMT - analogRead(TEMP_0_PIN)", current_raw);
log_int("_HEAT_MGMT - NUMTEMPS", NUMTEMPS);
#endif
// When using NEGATIVE SLOPE thermistor (option 1-7), when the heater is colder than targer temp, we get a higher analog reading than target,
// this switches it up so that the reading appears lower than target for the control logic.
if (THERMISTORHEATER != 8) current_raw = 1023 - current_raw; //O.V. 6/2014 this needs to be skipped for positive slope thermistor such as KTY84
pro podložku:
#ifdef BED_USES_THERMISTOR
current_bed_raw = analogRead(TEMP_1_PIN);
#ifdef DEBUG_HEAT_MGMT
log_int("_HEAT_MGMT - analogRead(TEMP_1_PIN)", current_bed_raw);
log_int("_HEAT_MGMT - BNUMTEMPS", BNUMTEMPS);
#endif
// If using NEGATIVE SLOPE thermistor (THERMISTORBED value 1-7), when the heater is colder than targer temp, we get a higher analog reading than target,
// this switches it up so that the reading appears lower than target for the control logic.
// current_bed_raw = 1023 - current_bed_raw; //commented out by O.V. 6/2014
if (THERMISTORBED!= 8) current_bed_raw = 1023 - current_bed_raw; //swithch the value only for options 1-7. Modified O.V. 6/2014
V thermistortables.h:
musíme nadefinovat novou kalibrační křivku pro KTY 84. Tím, že jsme v heater.cpp vynechali pro KTY84 termnistor opraci current_raw = 1023 - current_raw, chová se tento termistor vzhledem k programu stejně jako termistory s negativní teplotní závislostí. Kalibrační křivka musí být tedy napsná tak, že se vzrůstající tepltou hodnota klesá. Konkrétně pro 4.7kOhm odpor v serii s KTY84 termistrem dostáváme kalibrační křivku (první sloupec je digitální hodnota, druhý je teplota v C):
#if (THERMISTORHEATER == 8) || (THERMISTORBED == 8) // KTY84 positive slope thermistor (-55C to 300C)
#define NUMTEMPS_8 32
const short temptable_8[NUMTEMPS_8][2] = {
{662, 300},
{670, 290},
{678, 280},
{687, 270},
{696, 260},
{706, 250},
{715, 240},
{725, 230},
{735, 220},
{744, 210},
{754, 200},
{763, 190},
{773, 180},
{782, 170},
{792, 160},
{801, 150},
{811, 140},
{820, 130},
{829, 120},
{838, 110},
{847, 100},
{856, 90},
{865, 80},
{873, 70},
{882, 60},
{890, 50},
{898, 40},
{905, 30},
{909, 25},
{913, 20},
{920, 10},
{927, 0}, //safety
};
#endif
a konečně doplnit položky v seznamu termistorů pod kalibračními tabulkami:
pro extruder:
#elif THERMISTORHEATER == 8
#define NUMTEMPS NUMTEMPS_8
#define temptable temptable_8
a pro podložku:
#elif THERMISTORBED == 8
#define BNUMTEMPS NUMTEMPS_8
#define bedtemptable temptable_8
Takže nakonec to není ani tak moc složité. Snad jsem na nic nezapoměl.
27.6.2014
Docela dlouho mi trvalo, než jsem se dostal k další práci na tiskárně. Teprve předvčírem večer jsem měl pohromadě čas, dostatek životní energie a elánu, flash-disk s potřebnými soubory (který jsem předtím opakovaně zapomínal v práci...) a Bětčin notebook, který jsem používal k uploadování firmware do Sanguinololu již v minulosti. Postup prací byl následující:
1) Připojil jsem Sanguinololu s původním procesorerm přez 6-ti pinový ISP konektor a můj paralelní programátor k Ubuntu počítači (který jako jediný z našich domácích počítačů stále má paralelní port) a pomocí PonyProg2000 software přečetl paměť procesoru. To se zdařilo napoprvé a z toho jsem usoudil, že alespoň částečně je Atmel stále naživu.
2) Vycházel jsem však z teorie, že odešly vstupy/výstupy, pomocí kterých Atmel komunikuje se stepstick drivery. Vyměnil jsem tedy v Sanguinololu starý Atmel za nově zakoupený ATMega644 (GME electronic Praha, cca 225kč) a opět přes PonyProg do něj prve stažený obsah paměti starého čipu nahrál (příkazem "Write all"), což opět proběhlo bez problémů. Následně jsem připojil některé krokové motory z RepRapu a zkusil jimi pomoví Pronterface otočit. Bohoužel Sanguinololu s novým Atmelem se choval nachlup stejně jako s tím starým.
3) Přikročil jsem tedy k uploadování nové verze Sprinter firmaware s výše popsanými úpravami. Díky tomu, že jsem dříve do nového čipu nahrál obsah paměti toho starého, nemusel jsem již nahrávat bootloader. Upload se zdařil bez větších zádrhelů. Při opětovném testování krokových motorů se mi podařilo roztočit extruder. Opatrně jsem otestoval též teplotní stabilizaci trysky (nebyl jsem si 100% jist, že jsem správně naprogramoval podporu KTY84 termistoru). Vše se zdálo v pořádku, tak jsem popřipojoval všechny ostatní konektory a upevnil Sanguinololu zpět na tiskárnu.
4) Otestoval jsem pohyby všech os. Extruder běžel korektně oběma směry, nicméně X, Y, Z osy šly jen na jednu stranu. To mě chvíli mátlo, nicméně po troše testování jsem zjistil, že je to způsobené nastavením detekce dorazů ve firmware: Pro mechanické spínače je potřeba v souboru Configuration.h změnit false na true v následujících řádcích:
//If your axes move in one direction ONLY when the endstops are triggered, set [XYZ]_ENDSTOP_INVERT to true here:
const bool X_ENDSTOP_INVERT = true;
const bool Y_ENDSTOP_INVERT = true;
const bool Z_ENDSTOP_INVERT = true;
Poté už osy běhaly ochotně oběma směry.
6) Nastavil jsem motory osy Z tak aby opět byla tryska stejně vysoko nad podložkou na obou stranách základny. Během zkoušení a testování jsem totiž jednu stranu vytočil o trochu výš než druhou, takže mi jezdila trošku nakřivo. Testování provádím pomocí tenkého proužku kancelářského papíru, který zasunu mezi trysku a podložku, když je tryska v dolní pozici. Nastavuji dorazový vypínač na jedné straně a pozici Z-motoru na opačné straně tak aby na obou stranách byla tryska v takové pozici, že papírek mírně drhne mezi tryskou a podložkou, ale lze s ním pohybovat (to znamená tryska nesedí na podložce celou vahou)
7) Vytiskl jsem jednoduchý testovací objekt, abych se přesvědčil, že vše opět pracuje jak má. Možná bude ještě potřeba doladit teplotu trysky, protože současná kalibrační křivka pro KTY84 není možná totožná s tou, kterou jsem tam měl dříve (originál se nezachoval ...)
TAKŽE JSME OPĚT V TISKNOUCÍM MODU, SLÁVA!!
Co se vlastě přihodilo úplně přesně nevím. Zdá se, že nakonec chyba dost možná byla ve firmware, který se částečně přemazal (???). Dalo by se to potvrdit tím, že bych do Sanguinololu opět dal starý Atmel, nahrál do něj nový firmware a vyzkoušel zda bude fungovat. Ale v tuto chvíli se mi do toho nechce, když vše funguje.
Jsme otec (Ondřej) a syn (Kryštof) a v roce 2012 nás zaujal komunitní projekt 3D tiskáren RepRap natolik, že jsme si postavili jednu takovou pro vlastní potěšení. To bylo v době, kdy se u nás takových zařízení vyskytovalo jen pár a tak jsme pro informaci a inspiraci ostatních začali psát naše zkušenosti ze stavby a provozu a to v češtině, protože anglicky je na toto téma informací víc než dost.
středa 18. června 2014
středa 11. června 2014
Nebojte se AVR assembleru
Následující text jsem napsal již před pár lety pro svou vlastní pořebu jako takový obsáhlejší tahák, protože si po čase věci špatně pamatuji. A také případně pro další začínající programátory mikročipů Atmel. Třeba to bude pro někoho užitečné.
Stručný úvod do AVR assembleru
Ondřej Votava
Mikrokontroléry
Atmel AVR patří mezi velmi rozšířené mikročipy. Staly se
mimochodem základem v poslední době velmi populární platformy
Arduino. Na nejnižší úrovni se tyto jednočipy programují v AVR
assembleru, kde jednotlivé příkazy přímo odpovídají strojovým
instrukcím procesoru. Ačkoli pro tyto mikrokontroléry v současné
době existují též kompilátory upraveného jazyka c, pomocí
nichž lze psát přehledné strukturované programy, poskytuje
assembler jednoznačně nejlepší kontrolu daného hardware.
Assembler dává možnost určit doslova, co bude procesor vykonávat
v každém taktu jeho hodin (u AVR mikrokontrolerů s architekturou
RISC v podstatě odpovídá každému taktu procesoru jedna provedená
instrukce).
Cílem tohoto
dokumetu je poskytnout základní praktické vědomosti s
programováním v AVR assembleru pro mikrokontroléry Atmel. Nejedná
se o vyčerpávající příručku, ale o nástroj, který by vám
měl pomoci dostat se na prakticky použitelnou úroveň pokud možno
rychle. Snažím se jednak vysvětlit základní koncepty a zároveň
volit nejužitečnější a nejčastěji používanou podmnožinu
příkazů a direktiv assembleru s jejichž pomocí je možné začít
budovat vlastní programy. Asi nejlepší strategie je začít od
jednoduchých příkladů, které lze najít na internetu a ty
postupně upravovat a rozšiřovat.
Formáty čísel:
Základní jednotkou
ukládání informace u 8-mi bitových mikrokontrolérů je byte
o osmi bitech, který reprezentuje čísla od 0 do 255 (binárně
00000000 až 11111111). Větší zhusta používanou jednotkou je
tzv. word, neboli slovo, které sestává z dvou bytů, tedy
celkem 16 bitů a představuje čísla od 0 do 65535. Číselné
konstanty v assembleru mohou být psány v několika různých
formátech – jako čísla decimální, binární nebo
hexadecimální. Například decimální číslo 162 by v
hexadecimálním formátu bylo A2 a v binárním formátu 10100010.
Abychom rozlišili mezi těmito formáty, píše se v programu před
hexadecimální číslo 0x a před binární číslo 0b. Lze tedy
napsat 162 = 0xA2 = 0b10100010. Psaní čísla různým způsobem
nemá žádný vliv na to, jakým způsobem je číslo interpretováno
překladačem, ale pro čitelnost programu je často výhodnější
určitý formát. Nastavení jednotlivých bitů ve
vstupním/výstupním registru je jasnější v binárním formátu,
zatímco pro provádění matematických operací jsou pro nás
sdadnější decimální čísla.
Registry:
AVR mikrokontroléry mají
32 osmibitových paměťových jednotek, kterým se říká registry,
a ty jsou přímo přístupné pro CPU kontrolér pro použití v
celé řadě instrukcí.
V assembleru jsou jejich
původní názvy R0 až R31, nicméně pro lepší čitelnost
programu je možné každému registru přiřadit jiný název pomocí
direktivy:
.DEF name = Rx
S použitím této
direktivy je navíc daleko jednodušší změnit číslo registru
používaného pro určitý účel v celém programu naráz, pokud
zjistíme, že jiný register by se nám k danému účelu lépe
hodil. Tyto direktivy jsou zpravidla umístněny v záhlaví
programu, kde si podle potřeby nadefinujeme jména registrů, které
pak používáme podobně jako proměnné v jiných programovacích
jazycích.
Abychom mohli registry
prakticky použít, potřebujeme instrkukce, pomocí kterých je
možné do nich zapisovat data, nebo je je naopak z nich číst.
Dále potřebujeme provádět logické a aritmetické operace mezi
hodnotami uloženými v registrech, nebo také například být
schopni poslat hodnoty z registrů na výstupní porty kontroléru.
Záhy zjistíte, že registry R0 až R15 se chovají trochu odlišně
od registrů R16 až R31. Je to důsledkem architektury AVR
procesorů a specifika budou upřesněna pro jednotlivé příkazy,
kterých se to týká. Takže začněmě prvním úkolem, tedy
přiřazením konstatní hodnoty registru. K tomu se používá
příkaz:
LDI Rx, number
nebo
LDI name, number
kde name je název,
který jsme přiřadili danému registru pomocí .DEF direktivy.
Příkaz LDI lze použít pouze pro registry R16 až R32,
nikoli pro R0 až R15. Těmto registrům je třeba přiřazovat
hodnotu přesunutím (kopírováním) z jiného registru. K tomu se
používá příkaz MOV:
MOV Ry, Rx
Zde je obsah registru Rx
přepsán do Ry (hodnota v Rx se nezmění). Je všeobecné
pravidlo, že cílový registr (tedy ten registr, kam je zapisován
výsledek dané operace, tedy zde Ry) se píše hned za kód
instrukce. Abychom zapsali konstantu do registru R0 až R15 lze tedy
použít následující příkazy:
LDI R16, 150
;hodnota 150 je zapsána do R15
MOV R15, R16 ;nyní
hodnota 150 se zapíše z R16 do R15
Mimochodem, vše co se v
daném řádku objeví za středníkem považuje překladač za
komentář. Podle mojí zkušenosti čím víc komentářů tím
lépe. Když se budete k nějakému projektu vracet po čase,
oceníte, když je kód dobře okomentovaný. A to nemluvím o
případech, když máte přepisovat či upravovat kód po někom
jiném.
Porty:
Porty představují brány,
kterými procesor ovládá další části mikrokontroléru.
Například to jsou vstupní a výstupní porty, které ovládají
logické úrovně na pinech kontroléru, dále jsou to “control”
a “status” registry (poněkud matoucí označení, jsou to
funkčně vnitřní PORTY), které ovládají další funkce
mikrokontroléru. Některé nejběžnější příklady zde
probereme, nicméně je třeba zdůraznit, že takřka vždy je třeba
se při programování důkladně seznámit s detaily v manuálových
listech daného mikrokontroléru.
Obecně je opět třeba
vědět jak zapsat hodnotu do daného portu, případně jak přečíst,
jakou hodnotu daný port má. Instrukce OUT slouží k zapsání
hodnoty z registru do portu a má následující syntax:
OUT portname, Rx
Všiměte si, že
konstatntí hodnotu nelze přímo zapsat do portu, takže k tomu musí
být vždy použita dvoupříkazová struktura, jak je ukázáno v
příkladu, kde je binární číslo 10101010 zapsáno do výstupního
protu D:
LDI R16, 0b10101010
;load constant to R16
OUT PORTD, R16
;output register value to port
Zde používáme registr
R16 jako pomocný registr, do kterého je konstanta nejdřív
zapsána, a odkud se pak převede do portu. Je jakési nepsané
pravidlo, že právě R16 je užíván jako pomocný registr pro tyto
účely.
Bude užitečné se trochu
detailněji zastavit u portů, které ovládají vstupní a výstupní
piny mikrokontroléru. Množství vstupních a výstupních portů
záleží na specifickém typu mikrokontroléru. Například
oblíbený ATMega8 mikrokontrolér má tři porty ozačené jako B,
C, D kde porty B a D mají 8 bitů (ovládají 8 pinů) zatímco port
C má pouze 7 bitů (tedy ovládá 7 pinů mikrokontroléru). Každý
bit těchto portů představuje jeden pin konkrétní pin
mikrokontroleru. A každý jednotlivý pin může být použit buď
jako vstup, nebo jako výstup (pokud je použit jako vstup, pak lze
číst, zda je na tento pin přiloženo napětí, které odpovídá
logické 0 nebo logické 1). Pokud je použit jako výstup, je možno
ovládat výstupní napětí tak, že buď odpovídá logické 0 nebo
logické 1. To, zda je daný pin použit jako vstup nebo výstup je
určováno hodnotou zapsanou ve vnitřním registru zvaném
“data-direction-register – DDR”, jak je pro port D uvedeno v
následujícím příkladu:
LDI R16, 0b00001111 ;
hodnota masky se vloží do R16
OUT DDRD, R16
;write the mask to the DDR of port D.
Bity, které byly v DDR
nastaveny jako 1 jsou výstupy, piny nastaveny jako 0 jsou vstupy.
Hodnota výstupních pinů se pak dá nastavit opět pomící příkazu
OUT, tentokrát do portu, zvaného v tomto případě PORTD:
LDI R16, 0b00001010 ;
výstupní hodnoty se vloží do R16
OUT PORTD, R16
;zápis na výstupní piny portu D.
Na pinech pro bity 3 a 1
se nyní objeví 5V (logická 1), na pinech pro bity 2 a 0 je
nastaveno 0V, tedy logická 0.
Pro čtení hodnot na
pinech, které byly konfigurovány jako vstupy (a obecně pro čtení
hodnot z portů) slouží příkaz IN:
IN R16, PIND ;contents
of PIND is transferred to R16
Ještě jedna poznámka k
hardwaru vstpů/výstupů. Pokud je I/O port nastaven jako vstup, a
do příslušného bitu v PORTX je zapsána hodnota 1, jsou
aktivovány takzvané interní pull-up rezistory. Toto je užitečné,
když je tento vstup ovládán například tlačítkem připojeným
oproti zemi. Pokud je tlačítko stisknuté je na vstupu logická 0
(vstup je uzeměn), pokud není tlačítko stisknuté, interní
pull-up rezistor nastaví vstup na hodnotu logické 1.
Základní logické
operace: AND, ANDI, OR, ORI
Dvě základní logické
operace, AND a OR mají následující pravdivostní tabulky:
AND
- ABX111100010000
OR
- ABX111101011000
V AVR assembleru těmto
logickým operacím odpovídají příkazy AND a OR, které provedou
příslušnou logickou operaci pro jednotlivé bity ve
specifikovaných registrech. Jejich syntax je následující:
AND Ra, Rb
OR Ra, Rb
výsledek operace je podle
již zmíněné konvence zapsán do registru Ra. V následujícím
příkladu je operace AND použita k nastavení horních čtyř bitů
registru na hodnotu 0000, zatímco spodní čtyři byty zůstanou
nezměněny:
LDI R16, 0b10101010
;nacteme hodnotu do registru
LDI R17, 0b00001111
;toto je “maska” která určuje bity jsou k vynulování
AND R16, R17
; nulování horních čtyř bitů
;výsledek v R16 bude 0b00001010
Příkaz OR může být
použit podobným způsobem k nastavení specifických bitů na
hodnotu logické 1, zatímco ostatní bity jsou nezměněny.
Příkazy ANDI a ORI
pracují stejným způsobem, s tím rozdílem že druhým činitelem
není registr Rb, ale konstantní číslo. Takže uvedený příklad
může být také zapsán následujícím způsobem:
LDI R16, 0b10101010
;načtěte číslo do registru
ANDI R16, 0b00001111 ;
vynulují se horní čtyři bity
;výsledek v R16 bude 0b00001010
Větvení programu a
podprogramy
Pro efektivní
programování potřebujeme struktury pro větvení programu, tedy
pro podmíněné a nepodmíněné skoky a volání podprogramů. AVR
assembler má pro tento účel celou řadu příkazů. V
následujícím výčtu je vybráno pouze několik z nich, které
umožní programování širokou škálu bežných případů.
Detaily použití všech příkazů pro větvení lze najít v AVR
helpu a také v katakogových listech jednotlivých mikrokontrolerů.
RJMP – Relative JuMP
Jedná
se o nepodmíněný skok na nové místo v programu, které je
označeno návěstím (label) v assemblerovském programu. Nejlépe
se to ukáže na jednoduchém příkladě:
looplabel:
;toto je návěstí na které se skáče
RJMP looplabel
;příkaz tvoří nekonečnou smyčku.
BRNE – BRanch if Not
Equal (Skoč když není rovno nule)
Je prvním z rodiny
příkazů umožňujích podmíněné skoky, neboli větvení
programu.
Pokud provedení příkazu,
který předchází tomuto podmíněnému skoku má výsledek 0 v
cílovém registru (buď přičtením 1 k 255 nebo odečtením 1 od
1) je aktivován bit zvaný Zero-flag v stavovém registru CPU (CPU
status register). BRNE příkaz testuje, zda je tento bit aktivován
a pokud ne, provede skok na příslušné návěstí.
BRNE label
opět “label” značí
návěstí od kterého program pokračuje po provedení skoku. Pokud
však je podmínka splněna, t.j. Bit ve status registru je
aktivován, program pokračuje následující instrukcí. Opět
předvedeme na jednoduchém příkladě.
LDI R16, 0xFF ;do
registru R16 nahrajeme hodnotu 255
loop:
DEC R16
;snížíme hodnotu v R16 o 1
BRNE loop
;pokud hodnota v R16 není 0 skoč zpět na navesti loop
Zde jsme vytvořili
zpožďovací smyčku. Délka zpoždění závisí na počáteční
hodnotě v registru R16.
Komplementární příkaz
k BRNE je BREQ (BRanch when EQual – skoč když je rovno nule) a
existuje ještě řada dalších analogických příkazů pro
podmíněné skoky. Jejich funkci lze nalézt v katalogových
listech. S těmito dvěma se však pro začátek dá celkem vystačit.
SIBS and SIBC
Druhou skupinu větvicích
příkazů přestavují SIBS a SIBC (a celá řada dalších, které
patří do této rodiny). Podívejme se jak fungují. SBIS znamená
“Skip if Bit in I/O space is Set” tedy vynech instrukci pokud je
bit na vstupu nastaven na hodnotu 1. Tenot příkaz čte hodnotu bitu
n portu X a pokud je jeho hodnota 1, vynechá příští instrukci v
programu a pokračuje až tou další. Syntax tohoto příkazu je
následující:
SIBS portX, n
;přečte bit n v portu X
RJMP
label ;tato instrukce je vynechána poku je bit n 1
Analogicky
funguje příkaz SIBC “ Skip if Bit in I/O spce is CLEAR” tedy
vynech když je bit n v portu X vynulován. Tyto příkazy jsou
například velmi užitečné k testování, kdy je stisknuto ovadací
tlačítka na vstupu mikrokontroleru (případně jiný vnější
vstup je aktivován).
K
dispozici jsou i podobné příkazy, které místo se
vstupními/výstupními porty pracují s hodnotami v registrech.
Jsou to SBRS a SBRC a mají analogickou funkci a syntax. A na závěr
se je třeba zmínit ještě příkaz CPSE (porovnej hodnotu v R1 a
R2 a vynech příští instrukci pokud mají stejnou hodnotu). Syntax
tohoto příkazu je:
CPSE
R1 R2
Podprogramy
Používají
se velmi podobně jako v jiných programovacích jazycích. Jejich
syntax je snadná. Pro volání podprogramu je použit příkaz:
rcall
label
a
struktura podprogramu je:
label:
kód
podprogramu
ret
;tento příkaz značí konec podprogramu.
Použití paměti pro program k uložení konstantních dat:
Je
možné použít část paměti pro program k uložení dat,
například pro ascii řetězce či numerické konstanty. Je to
výhodné pro data která se často nemění, protože programovací
paměť má pouze omezený počet zápisů, výrobce specifikuje
10000. Datový blok lze zapsat přímo do assemblerovského programu
pomocí db (data block) direktivy:
label:
.db
data1, data2,
Návěstí “label”
určuje začátek datového bloku v paměti a je použito při čtení
dat z paměti pomocí příkazu LPM (Load Program Memmory – přečti
programovou paměť) jak je ukázáno v následujícím příkladu:
Do dvoubitového registru Z je třeba načíst pozici návěstí v
programové paměti a příkaz LPM potom načte hodnotu z této
pozici do registru R0. Následující příklad ukazuje jak je ascii
řetězec (kde každý znak je předsavován jedním bytem) přečten
z datového bloku a poslán na LCD monitor (pomocí podprogramu
lcddat). Datový blok začíná u návěstí “string”:
ldi ZH,high(2*string)
; Load high part of byte address into ZH
ldi ZL,low(2*string)
; Load low part of byte address into ZL
ldi chrcnt, 10 ;number
of characters to be read
charloop:
lpm
;load program memory at address in pointer Z to register R0
mov LCDbyte, r0 ;write
R0 to LCD byte
rcall lcddat
adiw ZL, 1
;increase value of program pointer Z by 1
dec chrcnt
brne charloop ;pokud
nebyl poslán poslední znak opakujte smyčku
.
.
.
;the data string hello
word follows.
string:
.db “Hello Word”
Přihlásit se k odběru:
Příspěvky (Atom)