OK

Při poskytování služeb nám pomáhají soubory cookie. Používáním našich služeb vyjadřujete souhlas s naším používáním souborů cookie. Více informací

Úvodní stránka » Arduino » Arduino v příkladech - IV.díl - Jak pracovat s pamětí podruhé

Arduino v příkladech - IV.díl - Jak pracovat s pamětí podruhé

Paměť SRAM si řídí Arduino samo. Vy řídíte pořadí zápisu a okamžik kdy Arduino paměť obsadí a kdy ji uvolní. V tomto díle se tedy budeme zabývat práci s SRAM, použití proměnných, optimalizací kódu a řízení využití této paměti. Také řešení pořadí zápisu se na první pohled může zdá jako zbytečné. Uvidíte že tato na první pohled nepodstatná činnost, může vést k pádu programu.

Pokud to myslíte s programování opravdu vážně, budete psát dlouhé kódy, využívat komunikaci po internetu, nebo i GSM modul, jsou tyto znalosti a jejich používání základem. Až vám program najednou z ničeho nic zamrzne, tak to obvykle mívá dva důvody. Buď jste vytvořili slepou uličku, typicky nenastane podmínka pro ukončení cyklu while respektive for, nebo došlo k přetečení SRAM paměti a to se špatně hledá.

Proměnné obecně

Zopakujeme si nejpoužívanější proměnné a jejich velikosti:

Název Rozsah Velikost
boolean (int8_t) true/false 8 bit (1 byte)
byte (int8_t) 0 - 255 8 bit (1 byte)
char (int8_t) -128 – 127 8 bit (1 byte)
unsigned char (uint8_t) 0 - 255 8 bit (1 byte)
int (int16_t) -32.768 - 32.767 16 bit (2 byte)
unsigned int (uint16_t) 0 - 65.535 16 bit (2 byte)
long (int32_t) -2.147.483.648 – 2.147.483.647 32 bit (4 byte)
unsigned long (uint32_t) 0 – 4.294.967.295 32 bit (4 byte)
float (int32_t) -3.4028235E+38 to 3.4028235E+38, 6-7 desetinných míst 32 bit (4 byte)

Všechny proměnné mají pevně stanovenou velikost. Kolik místa zaberou v paměti SRAM je určeno velikostí byte. Aby to nebylo jednoduché, toto platí pro mikročipy z rodiny AVR (Mega, UNO, Mikro..). Pro mikročipy z rodiny ARM (např. Due) jsou hodnoty jiné – int má 4 byte atd. a je potřeba použít deklaraci int…. viz údaje uvedené v závorce.

Pokud chcete vědět jak velké jsou proměnné u vás, nahrajte následující sketch:

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("sizeof(byte)=");
  Serial.println(sizeof(byte));

  Serial.print("sizeof(char)=");
  Serial.println(sizeof(char));

  Serial.print("sizeof(short)=");
  Serial.println(sizeof(short));

  Serial.print("sizeof(int)=");
  Serial.println(sizeof(int));

  Serial.print("sizeof(long)=");
  Serial.println(sizeof(long));

  Serial.print("sizeof(long long)=");
  Serial.println(sizeof(long long));

  Serial.print("sizeof(bool)=");
  Serial.println(sizeof(bool));

  Serial.print("sizeof(boolean)=");
  Serial.println(sizeof(boolean));

  Serial.print("sizeof(float)=");
  Serial.println(sizeof(float));

  Serial.print("sizeof(double)=");
  Serial.println(sizeof(double));

  Serial.print("sizeof(int8_t)=");
  Serial.println(sizeof(int8_t));

  Serial.print("sizeof(int16_t)=");
  Serial.println(sizeof(int16_t));

  Serial.print("sizeof(int32_t)=");
  Serial.println(sizeof(int32_t));

  Serial.print("sizeof(int64_t)=");
  Serial.println(sizeof(int64_t));

  Serial.print("sizeof(uint8_t)=");
  Serial.println(sizeof(uint8_t));

  Serial.print("sizeof(uint16_t)=");
  Serial.println(sizeof(uint16_t));

  Serial.print("sizeof(uint32_t)=");
  Serial.println(sizeof(uint32_t));

  Serial.print("sizeof(uint64_t)=");
  Serial.println(sizeof(uint64_t));

  Serial.print("sizeof(char*)=");
  Serial.println(sizeof(char*));

  Serial.print("sizeof(int*)=");
  Serial.println(sizeof(int*));

  Serial.print("sizeof(long*)=");
  Serial.println(sizeof(long*));

  Serial.print("sizeof(float*)=");
  Serial.println(sizeof(float*));

  Serial.print("sizeof(double*)=");
  Serial.println(sizeof(double*));

  Serial.print("sizeof(void*)=");
  Serial.println(sizeof(void*));

  while (1) delay(10000);
}

Existuje ještě proměnná String. Ta nemá definovanou velikost, proto je nebezpečná, její použití snadno vede k přetečení paměti a fragmentaci paměti. Je potřeba ji používat s rozmyslem a podle daných pravidel. Umožňuje však složité operace s řetězci textu a má nezastupitelnou roli. Ve skutečnosti se nejedná o proměnnou, ale o pole typu struct. V praxi s ní pracujete jako s proměnnou, která mění velikost podle toho co do ní vložíte.

Nezaměňujte pole char (arrays) a String. Např. Serial.print("Arduino") neobsahuje String, ale pole char.

Dále existuje deklarace pole např. int *var. Rozdíl od int var[x] je v tom, že x určuje velikost pole a tedy počet hodnot, které tam lze uložit, kdežto * umožňuje vložit tolik hodnot, kolik tam zadáte a tím je určena velikost. Z tohoto pohledu má tato deklarace stejné nectnosti jako String.

Pokud k programování používáte Arduino IDE, píšete program v jazyce Wiring a překladač při nahrávání přeloží program do jazyka C/C++, který položky byte, boolean a další nezná a pak byte a char je jedno a to samé. To připomínám jenom proto, aby jste se nedivili, že do boolean ve skutečnosti můžete zapsat cokoliv v rozsahu byte, aniž by došlo k přetečení hodnoty. Prostě definice boolean a další považujte za zjednodušení při psaní.

K proměnným zbývá už snad dodat:

  • unsigned znamená, že proměnná může nabývat pouze kladných hodnot
  • char se používá především k práci s tisknutelnými znaky. Jakmile s ní začnete pracovat, je zapotřebí myslet na to, že velikost bude o 1 větší, než je počet znaků.
  • unsigned char je stejná jako byte, používejte radši byte
  • float jako jediné umí pracovat s desetinnými čísly (tedy ještě to umí double, ta se vám hodí hned, jak začnete počítat rychlost světla)
  • boolean nabývá hodnot true a false tedy 1 a 0. V praxi však platí, že false = 0, jakákoliv jiná kladná, nebo záporná hodnota je true. Když se v programu budete ptát if(var == 1) a var bude cokoliv jiného než 1, odpověď tohoto dotazu bude false. Řešením je if(var == true). Odpovědí bude vždy true, pokud nebude var = 0.
  • když proměnné nepřiřadíte hodnotu, má se za to, že je rovna 0. Ale existuje i hodnota NULL – není to 0, NULL znamená že je proměnná prázdná. Jsou případy kdy se to hodí.

Deklarace proměnných

Pro deklaraci proměnné je možno využít dva způsoby.

int var; kdy int je typ, var je název proměnné. Takto definovaná proměnná má jasně stanovenou velikost a název a není jí hned zapotřebí přiřazovat hodnotu. Tato proměnná také lze definovat kdekoliv v průběhu programu, zrovna když jí potřebujete a kdykoliv můžete změnit hodnotu. Jak s ní pracovat si ukážeme dále.

#define var 8 je zvláštní typ proměnné, který lze definovat pouze v hlavičce programu. var je název proměnné, 8 je hodnota. U této proměnné je zapotřebí stanovit hodnotu, nemá definovanou velikost, nedává se za ní středník, neobsahuje rovná se a hlavně při běhu programu nelze měnit. Velikost si program určí sám v okamžiku kdy jí potřebujete.

Další a pro tento článek nejdůležitější rozdíl je v tom, že int… se uloží do paměti SRAM, kde zabere místo, #define… je uložen pouze v paměti FLASH a tím šetří místo v SRAM.

Existuje ještě instrukce const – const byte… const int… atd. Tato instrukce programu říká, že se jedná o konstantní proměnnou a při běhu programu nelze změnit. Z tohoto pohledu je podobná #define…, ale zabírá místo v SRAM. Její nevýhodou je, že v některých funkcích není vidět.

Proč používat const a #define. Jednoduše kvůli bezpečnosti. Jsou proměnné, které je zapotřebí ponechat beze změny a toto je řešení. Pokud se v průběhu programování dopustíte chyby a budete chtít změnit konstantní proměnnou, kompilátor vám oznámí chybu a program vůbec nenahraje.

Přetečení proměnných

Pro efektivní využití paměti SRAM je potřeba deklarovat proměnnou o velikosti kterou potřebujeme. Víme že hodnota bude v rozsahu 0-255, deklarujeme byte… ,bude v rozsahu do 32.767, deklarujeme int…, bude to text, deklarujeme char… , atd. Ušetřený 1 byte se může zdát jako zbytečný, ale jenom do té doby, než budete mít plnou SRAM a vracet se v programu zpátky je hrozná dřina.

Při deklaraci velikosti proměnné myslete dopředu. Pokud se do byte pokusíte přiřadit int, proměnná přeteče a dostanete neočekávaný výsledek. Pusťte si následující sketch a uvidíte.

byte byte_var1;
int int_var1 = 50;
int int_var2 = 28356;

void setup() {
  Serial.begin(9600);

  byte_var1 = int_var1;
  Serial.print("byte_var1 = ");
  Serial.println(byte_var1);

  byte_var1 = int_var2;
  Serial.print("byte_var1 = ");
  Serial.println(byte_var1);
}

void loop() {

}

Z příkladu vidíme, že pokud int je v rozsahu byte, hodnota se normálně přiřadí, pokud je mimo rozsah byte, ukáže nám jakési nesmyslné číslo. Není nesmyslné, provedl se výpočet byte = (int% * 256).

Úplně jiná situace je při práci s textem. Použijete deklaraci char proměnná[] – máte řetězec znaků. Aby program v paměti poznal kde končí, je nutno přiřadit na konec hodnotu NULL. Jinak Vám např. funkce print bude tisknou do té doby, dokud nenarazí na tento znak, v podstatě dojde k přetečení proměnné. V praxi se o to starat nemusíte, kompilátor vám tento znak přiřadí automaticky. Řetězec String, tento znak nepotřebuje. Podívejte na následující sketch.

char char_var[] = "To je 15 znaku.";
String string_var = "To je 15 znaku.";

void setup() {
  Serial.begin(9600);

  Serial.print("Delka char: ");
  Serial.println(sizeof(char_var));

  Serial.print("Delka String: ");
  Serial.println(string_var.length());
}

void loop() {

}

Vidíte že i když jsou texty stejně dlouhé, char je o 1 byte větší. Také vidíte, že pro určení velikosti char není použita funkce …lenght(), ale sizeof, která zase nejde použít u String. Tedy pokud vytvořím pole char var[8] mohu uložit pouze 7 znaků, osmý zabere NULL. Až začnete extrahovat texty, určitě si na tuto vlastnost vzpomeňte.

Viditelnost proměnných

Proměnné můžete deklarovat jako globální a lokální. Rozdíl popisuje následující sketch.

//globalni promenna je videt v ramci celeho programu
int global_A = 4; // to je globalni promenna
int global_B = 6; // to je globalni promenna
int global_Z;    // to je globalni promenna

void setup() {
  Serial.begin(9600);
}

void loop() {
  // secteme globalni promenne
  global_Z = global_A + global_B;
  Serial.print("Soucet globalnich promennych: ");
  Serial.println(global_Z);

  lokalni_promenna(); // spustime funkci, kde vyuzijeme lokalni promenne

  prenos_promenne(15, 15, 0); // spustime  funkci kam preneseme promenne a pouzijeme je jako lokalni

  while (1) delay(10000);
}

void lokalni_promenna() {
  // lokalni promenne jsou videt pouze v ramci funkce, kde jsou vytvoreny
  int local_A = 14; // to je lokalni promenna
  int local_B = 6;  // to je lokalni promenna
  int local_Z;      // to je lokalni promenna
  // opet secteme globalni promenne
  global_Z = global_A + global_B;
  Serial.print("Soucet globalnich promennych ve funkci: ");
  Serial.println(global_Z);

  // secteme lokalni promenne
  local_Z = local_A + local_B;
  Serial.print("Soucet lokalnich promennych ve funkci: ");
  Serial.println(local_Z);
}

void prenos_promenne(int local_A, int local_B, int local_Z) { // to jsou lokalni promenne
  // secteme prenesene hodnoty lokalni promenne
  local_Z = local_A + local_B;
  Serial.print("Soucet prenesenych lokalnich promennych ve funkci: ");
  Serial.println(local_Z);
}

Globální proměnná se deklaruje v záhlaví programu, vidíme ji všude. Lokální proměnná se deklaruje přímo ve funkci kde se používá a je vidět pouze ve funkci kde vyla vytvořena. Pokud lokální proměnnou použiji i jinde může se jmenovat stejně a ničemu to nevadí.

Super, ale píšeme článek o práci s SRAM. A tady je první skutečně efektivní příklad jak se pracuje v využitím SRAM:

  • globální proměnná obsadí SRAM hned při první spuštění programu a zabírá místo po celou dobu jeho běhu
  • lokální proměnná obsadí SRAM až v okamžiku, kdy ji vytvoří funkce a uvolní SRAM v okamžiku kdy funkce skončí

Práce s pamětí SRAM

Konečně jsme se dostali k jádru věci. Pojďme si popsat jednotlivé techniky, práce s pamětí SRAM.

Potřebuje tu proměnnou

Podívejte se na následující sketche a najděte dva rozdíly

int var1 = 4;
int var2 = 6;
int var3;
int var4;

void setup() {
  Serial.begin(9600);
}

void loop() {
  var3 = var1 + var2;

  if (var3 == 10) {
    var4 = analogRead(A0);
    Serial.print(var4);
  }
  
  while (1) delay(10000);
}

int var1 = 4;
int var2 = 6;

void setup() {
  Serial.begin(9600);
}

void loop() {

  if ((var1 + var2) == 10) {
    Serial.print(analogRead(A0));
  }

  while (1) delay(10000);
}

První sketch zabírá v pameti FLASH 2.356 byte a SRAM 192 byte. Druhý využije FLASH 2.340 byte a SRAM 188 byte. Proměnné var3 a var4, vůbec k ničemu nepotřebujeme. Výpočty můžeme provádět přímo v kódu. Program zrychlí a ušetříte místo. Ale POZOR, když čtete data z externích zdrojů, jako např. analogRead(), použijete ho vícekrát a očekáváte v rámci funkce stejnou hodnotu, rozhodně si proměnnou vytvořte. Výsledek čtení může být za dvě mikrosekundy jiný.

Přiřazení správné velikosti

Pokud deklarujete proměnné, dbejte na použití správné velikosti, tak jak jsme si popsali v části "Přetečení proměnných".

Použití lokální proměnné

Všude kde můžete, deklarujte ve funkcích lokální proměnné. Dokud ji nepoužijete, nezabírá v SRAM žádné místo, které můžete využít pro jiné proměnné.

Použití funkce (F)

Je to jednoduchá a velmi efektivní metoda na práci s textem. Spolehlivě pracuje pouze ve třídě print a println, která se používá při komunikaci po sériové lince (UART). Serial.print(), LCD.print(), GSM.print(). Finguje to tak, že řetězec, který potřebujete odeslat zabalíte a dáte předponu F.

Příklad: Serial.print(F(Tento řetězec vam v SRAM nezabere ani jeden byte))

Použití funkce PROGMEN

Tato funkce Vám zajistí, že proměnné zůstanou skutečně pouze v paměti FLASH. Její použití je však složitější. Vyplatí se však při práci s textem a pak s obsáhlými poli.

Tento sketch ukazuje úplně základní způsob jak pracovat s textovými řetězci. Při běhu programu si píšete sami který řetězec zobrazíte, v rámci jedné funkce stačí proměnné deklarovat pouze jednou, nebo je můžete zadat jako globální:

// deklarace promennych PROGMEM
const char text_0[] PROGMEM = "CERPADLO ZAPNUTO";
const char text_1[] PROGMEM = "CERPADLO VYPNUTO";
const char text_2[] PROGMEM = "PRAZDNA NADRZ";
const char text_3[] PROGMEM = "VODA VE SKLEPE";
const char text_4[] PROGMEM = "A UZ ME NIC CHYTREHO NENAPADA";

void setup() {
  Serial.begin(9600);
}

void loop() {

  // pripravime si promenne a urcime delku textu
  int len = strlen_P(text_0);
  char charText;

  // spustime cyklus pro nacteni jednotlivych znaku
  for (byte i = 0; i < len; i++)
  {
    charText =  pgm_read_byte_near(text_0 + i); // precteme jeden znak z PROGMEN
    Serial.print(charText); // a vytiskneme ho
  }

  while (1) delay(10000);
}

Já osobně jsem si vytvořil funkci, která zajistí načtení textu bez zbytečných řádků. Je jednoduchá a hlavně umožňuje dynamicky volat text, který právě potřebuji, jediným příkazem. Oproti předchozímu případu má nevýhodu, že si v paměti SRAM, hned po spuštěni programu, vymezí prostor o velikosti proměnné charText. To je daň za pohodlí.

// deklarace promennych PROGMEM
const char text_0[] PROGMEM = "CERPADLO ZAPNUTO";
const char text_1[] PROGMEM = "CERPADLO VYPNUTO";
const char text_2[] PROGMEM = "PRAZDNA NADRZ";
const char text_3[] PROGMEM = "VODA VE SKLEPE";
const char text_4[] PROGMEM = "Texty muzeme i skladat: ";
// vytvoreni poli textu PROGMEM
const char* const text_table[] PROGMEM = {
  text_0,  text_1,  text_2,  text_3,  text_4
};

void setup() {
  Serial.begin(9600);
}

void loop() {

  // za pomoci cyklu for zobrazime vsechny texty
  for (byte i = 0; i < 5; i++)
  {
    Serial.println(text_progmem(i)); // vytiskneme vsechna pole, jedno po druhém
  }

  //udelame nezeru
  Serial.println();

  // muzeme i texty skladat, napr. pro SMS spravu
  Serial.println(text_progmem(4) + text_progmem(0) + ", " + text_progmem(3));

  while (1) delay(10000);
}

// funkce pro cteni textu PROGMEM z pameti FLASH
String text_progmem(byte pole) { // ziskani textu z FLASH pameti pro zobrazeni
  char charText[30]; // POZOR - toto pole musí mít velikost jako nejvetsi pole PROGMEM +1
  strcpy_P(charText, (char*) pgm_read_word (&text_table [pole]));
  return (String)charText;
}

Funkci PROGMEM lze použít na jakýkoliv datový typ, který deklarujeme jako const, tedy nemůžeme ho v průběhu programu měnit.

Optimalizace vyrovnávací paměti

Inicializace Serial a jiných knihoven, si vytváří proměnné pro svoji práci. Obvykle tvůrci místem nešetří – ono totiž není úplně jejich vina a je to daň za univerzálnost. Pokud Vám stále chybí dostatek SRAM paměti, je možné upravit knihovny a snížit alokovanou vyrovnávací paměť, u knihoven na displeje vymazat nepoužívané fonty, nebo některé knihovny radši vůbec nepoužívat. Zde je také možné ušetřit mnoho paměti.

Stále SRAM nestačí

Udělali jste všechno možné a přesto paměť nestačí. Řešením je použít externí SRAM paměť. Ale to až jindy.

Jak si zobrazit volnou paměť

Existují knihovny, z nich každá vrací jiné hodnoty, některé počítají s jistou fragmentací, jiné ne. Chcete li jednoduchou metodu, použijte tuto funkci a na místa která Vás zajímají si vložte její volání a tisk výsledku hodnoty.

Serial.print(freeRam()); // volani funkce a zobrazeni hodnoty

// vlastni funkce
int freeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Fragmentace SRAM paměti

Stejně tak, jako vám fragmentuje pevný disk v počítači, při ukládání, přepisování a mazání dat, tak stejně fragmentuje paměť SRAM vašeho mikrokontroléru.

Jak to funguje? Když spustíte program, tak globální proměnné v pořadí tak jak jste je zadali, začnou obsazovat paměť SRAM. Každá proměnná dostane adresu paměti kde začíná, konec je určen její velikostí a tady začíná problém s fragmentací. Jakmile přidáte String, nebo *char array, program vyčlení nějaký datový prostor. Typicky String 6 byte. Opět se uloží začátek a konec adresy paměti a vše se zdá v pořádku. Pak si při běhu programu do String uložíte řetězec o 20 znacích. Mikrokontrolér neumí využít těch alokovaných 6 byte, pak něco přeskočit a zabrat si dalších 14 byte, potřebuje mít data uložená v celku, aby věděl kde má konec. Tedy najde první volné místo, kam se v souvislém zápisu vejde 20 byte a tam zabere nový prostor a původní uvolní. Protože je to globální proměnná už tento nový prostor neuvolní. A tak postupně vznikají různé díry, které se využívají a když jich je moc, program zamrzne. Může hned, nebo také za měsíc.

Řešení tohoto problému je více. Ten nejjednodušší je omezit proměnné, které mohou dynamicky měnit svou velikost, a když už jí používáte, hned na začátku jí rezervovat dostatečný prostor.

char char_text_0[] = "CERPADLO ZAPNUTO";
String string_text;

void setup() {
  Serial.begin(9600);
  string_text.reserve(30); // alokujeme pamet o velikosti 30 byte
}

void loop() {

  string_text = char_text_0; // priradime text
  Serial.println(string_text);

  zobrazText();

  while (1) delay(10000);
}

void zobrazText() {
  String text; // vztvorime lokalni promennou
  text.reserve(30); // alokujeme pamet o velikosti 30 byte
  text = char_text_0; // priradime text
  Serial.println(text);
}

Funkce reserve() zajistí, že další proměnná se vytvoří až za rezervovaným prostorem. Tento rezervovaný prostor můžeme využívat dle potřeb, ale nesmíte ho překročit. Pak budete tam kde na začátku a ještě jste si zabrali prostor.

Další způsobem omezení fragmentace je maximální využívání lokálních proměnných, jak jsme si řekli dříve.

Konečně konec, že ano. Tomuto tématu předchází mnoho jiných znalostí, jako je práce s textem, porovnávání proměnných, přetypování, převody datových typů a další, které pomohou optimalizovat jak FLASH tak SRAM paměť, ale to bychom byli stále někde na začátku tohoto článku. Někdy na to jistě vyjde čas.

V příštím díle si ukážeme jak pracovat s pamětí EEPROM.

JB

jaroslav.bohac@arduinotech.cz

Přidat komentář

Zvýrazněné položky jsou povinné.

Přehled komentářů

  1. Poděkování (Jaromír Zeman, 18.4.2016 9:27:30) Odpovědět

    Dobrý den, velice pěkný článek. Pro začátečníky ideální.
    Velice pěkně vysvětleno.
    Děkuji a těším se na další díly.

  2. Poděkování (LuBoss, 4.5.2016 10:44:07) Odpovědět

    Moc pěkně vysvětleno pro jednoduché pochopení.
    Konečně mi došlo, že F("nějaký text") je jenom makro v podobě vychytávky pro funkce *.print a že je to vlastně čtení z PROGMEM uloženého textu ve flash. Fajn je vysvětlení že PROGMEM lze použít univerzálně, ale při použití je třeba data přečíst a zkopírovat do nějaké lokální proměnné a to vlastním kódem.

    Možná by byla praktická zmínka o tom, odkud se vyčarují příkazy 'strlen_P' a 'pgm_read_byte_near' (pgmspace.h).
    Také se často vyskytuje v příkladech PSTR (ukazatel na string ve flash). Jak je to s jeho použitím?

    Příkazy strlen_P a desítky dalších, jsou součástí knihoven String.h, studio.h, pgmspace.h....., které jsou součástí základní instalace Arduino_IDE a najdete je hluboko v kořenovém adresáři IDE. Jedná se o funkce jazyka C a samozřejmě jsou i velmi dobře popsány hlavně na anglicky psaných webech. Rád bych zveřejnil více zdrojových informací, které už někdo kompletně popsal. Nemám souhlas vlastníků těchto webů a nechci se dostat do sporu s autorskými právy. Když někdo něco napíše je to jeho. Z toho plyne, že odkazy zveřejnit nemohu. To je i důvod proč všechny kódy v těchto seriálech píši od základu sám a nekopíruji ani písmenko. Použijte velkého bratra Google. Zkuste dotaz The C Library Reference Guide. Funkce PSTR je vysvětlena na http://playground.arduino.cc/Main/Printf (to je jediný odkaz, který mohu bez obav zveřejnit). Jestli jste začátečník, pusťte tuto funkci z hlavy. K ničemu Vám nebude. Dům se od střechy nestaví.

    JB

  3. CONST (LuBoss, 14.10.2016 11:13:21) Odpovědět | Zobrazit odpovědi

    Opravdu deklarovaná konstanta pomocí 'const' zabírá i místo v SRAM?

    1. Re: CONST (JB, 24.10.2016 23:40:58) Odpovědět

      "const" je deklarace globální proměnné a všechny globální proměnné, bez ohledu na typ deklarace se nahrají do SRAM při spuštění programu. Jediné, které se nenahrávají jsou proměnné deklarované jako lokální (vytváří se při spuštění funkce, kde je zapotřebí je naplnit daty a zanikají při ukončení funkce a zase jsou tady výjimky jak z lokálních udělat globální, pak ale zůstanou v SRAM) a pak proměnné PROGMEM. Tyto ale zase nejde deklarovat jako lokální, nemá to žádný smysl. Existují ještě vychytávky jak nechat jinak globální proměnnou mimo SRAM, ale to už je jako drbání se levou rukou za pravým uchem.

  4. SD (Tomas, 9.2.2017 13:59:31) Odpovědět | Zobrazit odpovědi

    Není proto lepší tyto informace ukládat na SD kartu ?

    1. Re: SD (Jaroslav Boháč, 9.2.2017 22:54:44) Odpovědět

      Na nic neexistuje jednoznačná odpověď. SD karta je velmi nespolehlivé zařízení a taky pekelně pomalé. Ukládat tam proměnné rozhodně nedoporučuji. Pokud dojde k poškození dat (kartě SD opravdu přepisy hodně vadí) program se zablokuje. To v případě uložení dat přímo do programu nehrozí. Na druhou stranu, mám-li nějaké texty, které budu zobrazovat např. na displeji jen občas, nebo načítat webovou stránku, kterou naplním daty např. z čidel, je karta ideálním řešením jak ušetřit v paměti Arduina spoustu místa. Také karta SD je velice vhodná pro ukládání logů - třeba jednou za hodinu přehled načtených teplot. Vždy jde pouze o volbu cesty, která je pro každý projekt jiná. Jenom program pro zprovoznění SD a potřeba alokace dostatku volné paměti zabere samo o sobě spoustu místa.

      V jednom z následujících dílů se tímto zabývat.

      1. Re: Re: SD (Tomas, 10.2.2017 14:34:23) Odpovědět

        Velice děkuji .. určitě si rád na další skvělý tutoriál počkam.
        Děkuji

  5. Experiment (Mira, 13.2.2017 20:00:30) Odpovědět | Zobrazit odpovědi

    Co kdyz zalozim funkci typu boolean a nepriradim zadnou navratovou hodnotu? napriklad:
    Setup {
    if (BFunction()) {
    Serial.print("TRUE");
    } else {
    Serial.print("FALSE");
    }
    }

    boolean BFunction() {
    }

    Na Windows kompilaci mi vrati TRUE
    Na Linuxove kompilaci mi vrati FALSE
    Proc to?

    1. Re: Experiment (Jaroslav Boháč, 21.2.2017 19:29:50) Odpovědět

      Proč to jednou vrátí TRUE a jednou FALSE. Možná proto, že neví co má vrátit, vy to po něm chcete a tak vrátí co se mu zrovna hodí - přece se ptáte.

      Na druhou stranu nevidím důvod proč vytvářet návratovou funkci, která nevrací žádnou hodnotu. Programově je to úplně špatně, je povinností programátora zajistit, aby se vždy nějaká hodnota vrátila. Tedy je-li to takto napsané, považoval bych to za chybu programátora.

      Co mě spíše zaráží (teď otestováno) kompilátor mi program nenahrál a vrátil chybu - že funkce nevrací hodnotu. Zkuste jinou verzi kompilátoru.


TOP produkty

Arduinotech GSM shield

Arduinotech GSM shield
877 Kč s DPH

Arduino MEGA2560

Arduino MEGA2560
424 Kč s DPH

Kontakt

Ing. Petr Foltýn
Kunčice pod Ondřejníkem 814, 73913
TOPlist