Operácie

AVR - Definície konštánt: Rozdiel medzi revíziami

Zo stránky SensorWiki

Balogh (diskusia | príspevky)
Vytvorená stránka „Spracované podľa Stackoverflow<ref group="REF">Jonathan Leffler: “static const” vs “#define” vs “enum” Available online:https://stackoverflow.com/questions…“
 
Balogh (diskusia | príspevky)
dBez shrnutí editace
 
(4 medziľahlé úpravy od rovnakého používateľa nie sú zobrazené.)
Riadok 1: Riadok 1:
Spracované podľa Stackoverflow<ref group="REF">Jonathan Leffler: “static const” vs “#define” vs “enum” Available online:https://stackoverflow.com/questions/1674032/static-const-vs-define-vs-enum .</ref>  
Spracované z niekoľkých zdrojov<ref group="REF">Jonathan Leffler: “static const” vs “#define” vs “enum” Available online: https://stackoverflow.com/questions/1674032/static-const-vs-define-vs-enum </ref><ref group="REF">James Lewis: Const vs #define – When do you use them and why? Available online: https://www.baldengineer.com/const-vs-define-when-do-you-them-and-why.html?</ref> na internete.
 


Niekde sa na definície konštánt (napr. pripojenie periférí ku konkrétnym pinom používa
Niekde sa na definície konštánt (napr. pripojenie periférí ku konkrétnym pinom používa
  static const int led = 13;
  static const int led = 13;             (1)
alebo  
alebo  
  #define led 13
  #define led 13                         (2)
alebo niekde aj  
alebo niekde aj  
  enum { led = 13 };
  enum { led = 13 };                     (3)


Odpoveď je rôzna v závislosti od toho, či používame jazyk C++ alebo C. Pre C++ platí prvá možnosť, vždy použiť const.
Odpoveď je rôzna v závislosti od toho, či používame jazyk C++ alebo C. Pre C++ platí prvá možnosť, vždy použiť const.
Riadok 19: Riadok 18:




== Prečo použiť #define ==
Treba si uvedomiť, že #define nie je príkaz, ale tzv. makro. To znamená, že ešte pred samotným prekladom programu tzv. preprocesor nahradí všetky výskyty textu v kóde.
Napríklad
#define ledPin 13
sa nahradia všetky výskyty textu ledPin číslom 13. Výhodou je, že premenná ledPin neobsadí žiadne miesto v cennej RAM pamäti. Dokonca vlastne žiadna premenná ledPin ani nevznikne.
== Kedy použiť const ==
Ak použijeme modifikátor const, hovoríme kompilátoru, že daná premenná sa nebude (a ani nesmie) meniť. Obvykle sa vytvorí premenná v pamäti RAM a to aj s príslušným pointrom na túto premennú. Avšak prekladač avr-gcc  väčšinou  pochopí, že takýto kúsok kódu má preložiť tak, aby neobsadil žiadnu pamäť RAM.
Napríklad
const int ledPin=13;
neobsadí v pamäti tiež žiadne miesto.
Ak si to chcete overiť a vyskúšať, dá sa to z príkazového riadku
avr-size -C sketch_nov11a.cpp.elf
alebo v konzole pri preklade v AVR studiu.
== Špecifikum AVR: Uloženie dát do pamäte programu (Flash) ==
Pri programovaní mikrokontrolérov AVR (napr. ATmega328) je dôležité si uvedomiť, že používajú '''Harvardskú architektúru'''. To znamená, že pamäť pre program (Flash) a pamäť pre dáta (SRAM) sú fyzicky oddelené.
=== Problém s ''const'' v RAM ===
V bežnom C na PC (Von Neumannova architektúra) sú dáta aj kód v spoločnom adresnom priestore. Na AVR je to inak. Ak napíšete bežnú definíciu konštantného poľa alebo reťazca:
<source lang="c">
const char oznam[] = "Tento text zaberá zbytočne veľa miesta";
</source>
Kompilátor tento reťazec síce uloží do Flash pamäte (aby sa uchoval po vypnutí napájania), ale '''pri štarte programu ho automaticky skopíruje do pamäte RAM'''.
* '''Dôvod:''' Inštrukcie procesora pre prácu s dátami (LD, ST, atď.) dokážu pristupovať len do RAM.
* '''Dôsledok:''' Plytváte cennou operačnou pamäťou. ATmega328 má len 2 KB RAM. Ak definujete niekoľko dlhších textov pre LCD displej, pamäť sa rýchlo zaplní a program môže začať kolabovať (pretečenie zásobníka/stacku).
=== Riešenie: Kľúčové slovo PROGMEM ===
Aby ste ušetrili RAM, musíte kompilátoru explicitne prikázať, aby dáta ponechal '''iba''' vo Flash pamäti a nekopíroval ich pri štarte. Na to slúži modifikátor <code>PROGMEM</code> a knižnica <code><avr/pgmspace.h></code>.
<source lang="c">
#include <avr/pgmspace.h>
// Tento reťazec ostane len vo Flash pamäti a nezaberá RAM
const char oznam[] PROGMEM = "Tento text je uložený efektívne";
</source>
=== Čítanie dát z Flash pamäte ===
'''Pozor!''' Keďže sú dáta len vo Flash pamäti, nemôžete k nim pristupovať ako k bežným premenným. Štandardné C pointre ukazujú do RAM. Ak by ste skúsili prečítať premennú <code>oznam[0]</code> bežným spôsobom, prečítali by ste náhodnú hodnotu z RAM na rovnakej adrese.
Na čítanie musíte použiť špeciálne funkcie, ktoré využívajú inštrukciu LPM (Load Program Memory):
* <code>pgm_read_byte(adresa)</code> - pre čítanie 1 bajtu (char, uint8_t)
* <code>pgm_read_word(adresa)</code> - pre čítanie 2 bajtov (int, uint16_t)
'''Príklad použitia:'''
<source lang="c">
#include <avr/io.h>
#include <avr/pgmspace.h>
const char text_flash[] PROGMEM = "Hello World";
void vypis_znak(void) {
    char pismeno;
   
    // ZLE: Prečíta nezmysel z RAM
    // pismeno = text_flash[0];
    // DOBRE: Prečíta bajt z Flash pamäte
    pismeno = pgm_read_byte(&(text_flash[0]));
}
</source>
=== Inline reťazce (Makro PSTR) ===
Ak potrebujete použiť reťazec priamo vo volaní funkcie a nechcete ho definovať ako globálnu premennú, môžete využiť makro <code>PSTR()</code>:
<source lang="c">
// Funkcia, ktorá očakáva pointer do Flash pamäte
odosli_retazec_z_flash(PSTR("Tento text nezaberá RAM"));
</source>


==References==
==References==

Aktuálna revízia z 08:49, 15. február 2026

Spracované z niekoľkých zdrojov[REF 1][REF 2] na internete.

Niekde sa na definície konštánt (napr. pripojenie periférí ku konkrétnym pinom používa

static const int led = 13;              (1)

alebo

#define led 13                          (2)

alebo niekde aj

enum { led = 13 };                      (3)

Odpoveď je rôzna v závislosti od toho, či používame jazyk C++ alebo C. Pre C++ platí prvá možnosť, vždy použiť const. V jazyku C odpoveď záleží od použitia:

  • Ak potrebujeme aj pointer, tak musíme použiť (1),
  • pretože pre (2) pointer nevznikne.
  • Možnosti (1) a (3) vytvoria aj symbol pre debugger, možnosť (2) nie.
  • Ak potrebujeme určiť rozmer poľa alebo reťazca, nedá sa použiť (1).
  • Ak chceme použiť konštantu do príkazu switch, nedá sa použiť (1)
  • Ak chceme inicializovať statickú premennú, nemôžeme použiť (1)


Prečo použiť #define

Treba si uvedomiť, že #define nie je príkaz, ale tzv. makro. To znamená, že ešte pred samotným prekladom programu tzv. preprocesor nahradí všetky výskyty textu v kóde. Napríklad

#define ledPin 13

sa nahradia všetky výskyty textu ledPin číslom 13. Výhodou je, že premenná ledPin neobsadí žiadne miesto v cennej RAM pamäti. Dokonca vlastne žiadna premenná ledPin ani nevznikne.


Kedy použiť const

Ak použijeme modifikátor const, hovoríme kompilátoru, že daná premenná sa nebude (a ani nesmie) meniť. Obvykle sa vytvorí premenná v pamäti RAM a to aj s príslušným pointrom na túto premennú. Avšak prekladač avr-gcc väčšinou pochopí, že takýto kúsok kódu má preložiť tak, aby neobsadil žiadnu pamäť RAM. Napríklad

const int ledPin=13;

neobsadí v pamäti tiež žiadne miesto.


Ak si to chcete overiť a vyskúšať, dá sa to z príkazového riadku

avr-size -C sketch_nov11a.cpp.elf

alebo v konzole pri preklade v AVR studiu.


Špecifikum AVR: Uloženie dát do pamäte programu (Flash)

Pri programovaní mikrokontrolérov AVR (napr. ATmega328) je dôležité si uvedomiť, že používajú Harvardskú architektúru. To znamená, že pamäť pre program (Flash) a pamäť pre dáta (SRAM) sú fyzicky oddelené.

Problém s const v RAM

V bežnom C na PC (Von Neumannova architektúra) sú dáta aj kód v spoločnom adresnom priestore. Na AVR je to inak. Ak napíšete bežnú definíciu konštantného poľa alebo reťazca:

const char oznam[] = "Tento text zaberá zbytočne veľa miesta";

Kompilátor tento reťazec síce uloží do Flash pamäte (aby sa uchoval po vypnutí napájania), ale pri štarte programu ho automaticky skopíruje do pamäte RAM.

  • Dôvod: Inštrukcie procesora pre prácu s dátami (LD, ST, atď.) dokážu pristupovať len do RAM.
  • Dôsledok: Plytváte cennou operačnou pamäťou. ATmega328 má len 2 KB RAM. Ak definujete niekoľko dlhších textov pre LCD displej, pamäť sa rýchlo zaplní a program môže začať kolabovať (pretečenie zásobníka/stacku).

Riešenie: Kľúčové slovo PROGMEM

Aby ste ušetrili RAM, musíte kompilátoru explicitne prikázať, aby dáta ponechal iba vo Flash pamäti a nekopíroval ich pri štarte. Na to slúži modifikátor PROGMEM a knižnica <avr/pgmspace.h>.

#include <avr/pgmspace.h>

// Tento reťazec ostane len vo Flash pamäti a nezaberá RAM
const char oznam[] PROGMEM = "Tento text je uložený efektívne";

Čítanie dát z Flash pamäte

Pozor! Keďže sú dáta len vo Flash pamäti, nemôžete k nim pristupovať ako k bežným premenným. Štandardné C pointre ukazujú do RAM. Ak by ste skúsili prečítať premennú oznam[0] bežným spôsobom, prečítali by ste náhodnú hodnotu z RAM na rovnakej adrese.

Na čítanie musíte použiť špeciálne funkcie, ktoré využívajú inštrukciu LPM (Load Program Memory):

  • pgm_read_byte(adresa) - pre čítanie 1 bajtu (char, uint8_t)
  • pgm_read_word(adresa) - pre čítanie 2 bajtov (int, uint16_t)

Príklad použitia:

#include <avr/io.h>
#include <avr/pgmspace.h>

const char text_flash[] PROGMEM = "Hello World"; 

void vypis_znak(void) {
    char pismeno;
    
    // ZLE: Prečíta nezmysel z RAM
    // pismeno = text_flash[0]; 

    // DOBRE: Prečíta bajt z Flash pamäte
    pismeno = pgm_read_byte(&(text_flash[0])); 
}

Inline reťazce (Makro PSTR)

Ak potrebujete použiť reťazec priamo vo volaní funkcie a nechcete ho definovať ako globálnu premennú, môžete využiť makro PSTR():

// Funkcia, ktorá očakáva pointer do Flash pamäte
odosli_retazec_z_flash(PSTR("Tento text nezaberá RAM"));

References

  1. Jonathan Leffler: “static const” vs “#define” vs “enum” Available online: https://stackoverflow.com/questions/1674032/static-const-vs-define-vs-enum
  2. James Lewis: Const vs #define – When do you use them and why? Available online: https://www.baldengineer.com/const-vs-define-when-do-you-them-and-why.html?