Arduino je revolucioniral svet elektronike zahvaljujoč lahkoti, s katero je mogoče ustvariti funkcionalne prototipe in projekte. Vendar pa za tiste, ki želijo svoje programiranje narediti korak dlje in optimizirati vire, ohraniti kodo čisto in povečati učinkovitost, makri postane ključno orodje.
V tem članku se bomo poglobili v uporabo makri v Arduinu: kaj so, kako se uporabljajo, njihove prednosti in omejitve. In to bomo storili z zbiranjem najobsežnejših in najbolj uporabnih informacij iz najboljših virov, ki so na voljo na spletu, prepisanih na jasen in sodoben način, da bodo resnično praktični.
Kaj so makri v Arduinu?
Makri so direktive predprocesorja v C/C++, ki vam omogočajo zamenjavo besedila, preden je koda prevedena. Namesto izvajanja navodil kot tradicionalna funkcija, makro deluje tako, da zamenja dele izvornega besedila, ki je neposreden vpliv na to, kako se ustvari končna binarna koda.
Predprocesor Zažene se pred dejansko kompilacijo in je odgovoren za uporabo teh zamenjav. V Arduinu to omogoča od definirati konstante, pogojno vključite datoteke ali celo ustvarite majhne spletne funkcije ki prihranijo čas in spomin.
Osnovni primer: definicija kot #define LED_PIN 13
povzroči, da se vsa koda samodejno zamenja LED_PIN
jo 13
pred sestavljanjem.
To se morda zdi trivialno, vendar ponuja a zmogljiv način za pisanje bolj prilagodljive in vzdržljive kode.
Prednosti uporabe makrov
Implementacija makrov v projektih Arduino lahko ponudi številne posebne prednosti:
- Izboljšajte berljivost kode: S ponovno uporabo simbolnih imen je lažje razumeti namen vsakega elementa.
- Optimizirajte delovanje: Ker ne generirajo funkcijskih klicev, lahko makri hitreje izvajajo operacije.
- Zmanjšajte porabo RAM-a: še posebej uporabno na ploščah z omejenimi viri, kot je Arduino UNO.
- Omogoča pogojne prilagoditve: Možno je sestaviti različne fragmente kode, odvisno od vrste uporabljene plošče Arduino.
Osnovni makri: uporaba #define
Direktive #defini Je najbolj uporabljen. Uporablja se tako za določite konstantne vrednosti kar se tiče ustvarite vstavljene samodejne funkcije v času pred kompilacijo.
Primer 1: Določite žebljiček
#define PINLED 13
void setup() {
pinMode(PINLED, OUTPUT);
}
void loop() {
digitalWrite(PINLED, HIGH);
delay(500);
digitalWrite(PINLED, LOW);
delay(500);
}
Primer 2: Makro kot vgrajena funkcija
int itemCounter = 0;
#define COUNT_ITEM() do { itemCounter++; } while(0)
void setup() {
Serial.begin(9600);
COUNT_ITEM();
COUNT_ITEM();
}
void loop() {
Serial.println(itemCounter);
}
Kot lahko vidite, uporaba vzorca stori { … } medtem ko(0) zagotavlja, da se makro obnaša varno, tudi če se uporablja znotraj pogojnih struktur.
## operator in združevanje makrov
Operator ## je zmogljivo predprocesorsko orodje. ki omogoča veriženje identifikatorjev. To je zelo uporabno, če želite dinamično generirati imena spremenljivk.
Praktični primer:
#define GENERAR_VARIABLE(no) \
int var##no = no;
void setup() {
GENERAR_VARIABLE(3); // crea int var3 = 3
}
Pomembno opozorilo: Ta operater ni enako združljiv z vsemi modeli plošč Arduino. Na primer, lahko deluje dobro na Uno ali Esplora, vendar ne uspe na Mega. Prav tako ne morete ugnezditi ustvarjanja makra v druge makre neposredno z ##.
Makri in varčevanje s pomnilnikom
Ena od ključnih prednosti uporabe makrov v Arduinu je varčevanje z RAM-om. Arduino ima omejeno zmogljivost, zato lahko nalaganje besedilnih nizov neposredno v RAM postane velik problem.
Napredna tehnika za preprečevanje tega vključuje uporabo FORCE_INLINE in naloži nize iz programskega pomnilnika (PROGMEM):
#include <HardwareSerial.h>
#define MYSERIAL Serial
#define FORCE_INLINE __attribute__((always_inline)) inline
FORCE_INLINE void printFromFlash(const char *str) {
char ch = pgm_read_byte(str);
while (ch) {
MYSERIAL.write(ch);
ch = pgm_read_byte(++str);
}
}
#define SERIAL_LOG(x) (MYSERIAL.print(x))
#define SERIAL_LOGLN(x) (MYSERIAL.println(x))
Uporaba teh makrov lahko vpliva na to, ali projekt deluje ali ne, zlasti v aplikacijah z zasloni ali več senzorji.
Makri v kombinaciji s funkcijami
Makri lahko tudi olajšajo dinamično klicanje funkcij na podlagi vrste, posredovane kot parameter. Jasen in precej nazoren primer je:
#define FUNC_LENTA(tipo) \
{ funcion_##tipo##_lenta(); }
#define FUNC_RAPIDA(tipo) \
{ funcion_##tipo##_rapida(); }
void funcion_caminar_lenta() {
Serial.println("Andando despacio");
}
void funcion_caminar_rapida() {
Serial.println("Andando rápido");
}
void setup() {
Serial.begin(9600);
FUNC_LENTA(caminar);
}
void loop() {
FUNC_RAPIDA(caminar);
}
Zahvaljujoč operatorju ## in makrom se lahko izognemo ponavljajočim se strukturam in centraliziramo dinamično logiko..
Makri z izhodnimi parametri
Možna je tudi uporaba makrov za inkapsulacijo majhnih predmetov ali pretvorb:
#define BOOL_OUT() (bool){false}
#define NUM_OUT(a,b) (float){a+b}
#define STR_OUT(msg) (String){msg}
void loop() {
Serial.println(BOOL_OUT());
Serial.println(NUM_OUT(1.2, 3.4));
Serial.println(STR_OUT("Mensaje"));
}
Dobre prakse in previdnostni ukrepi z makri
Prekomerna ali neprevidna uporaba makrov lahko povzroči napake, ki jih je težko odpraviti. Na primer z nepravilnimi zamenjavami ali definiranjem imen, ki so v nasprotju z imeni v zunanjih knjižnicah.
Nekaj osnovnih pravil za preprečevanje težav:
- Izogibajte se nepotrebnim presledkom ali prelomom vrstic znotraj makra.
- Ne vključujte komentarjev znotraj kompleksnih makrov, ki uporabljajo več vrstic.
- Uporabite edinstvena imena ali s predponami (kot je ime projekta), da se izognete sporom.
- Zamenjajte makre z resničnimi konstantami ali funkcijami kadar koli je to mogoče. Sodobni C++ omogoča čistejše in varnejše alternative.
Po drugi strani pa lahko prekomerna uporaba makrov zmanjša jasnost kode. Cilj bi moral biti izboljšanje učinkovitosti in modularnosti brez ogrožanja vzdržljivosti.
Pogojne direktive in prilagodljivo prevajanje
Ena najbolj praktičnih funkcij v razširljivih projektih je uporaba makrov za pogojno ustvari kodo, nekaj zelo uporabnega, ko želite, da ista skica deluje na različnih ploščah.
Tipičen primer:
#ifdef ARDUINO_MEGA
#define LEDPIN 53
#else
#define LEDPIN 13
#endif
Uporaben je tudi za nadzor odpravljanja napak ali prikazovanje sporočil prevajalnika #pragma sporočilo ali celo ustvarjajo napake pod določenimi pogoji #napaka.
Notranji makri prevajalnika
Predprocesor GCC za AVR (uporabljen v Arduinu) vključuje več posebni makri, ki zagotavljajo sistemske informacije, zelo uporaben med razvojem:
- __LINE__: številka trenutne vrstice.
- __DATOTEKA__: ime trenutne datoteke.
- __TIME__ in __DATE__: čas in datum kompilacije.
- __func__: ime trenutne funkcije.
Omogočajo nadzor različic, strukture dnevnika ter olajšajo vzdrževanje in sledenje napakam brez vdora v glavno kodo.
Makri ponujajo močan in prilagodljiv način za strukturiranje projektov Arduino. Omogočajo vam definiranje konstant, prihrani spomin, prilagodite kodo odvisno od izvajalnega okolja in ustvari bloke za večkratno uporabo brez podvajanja vrstic. Seveda zahtevajo disciplino, jasnost in znanje, da bi se izognili subtilnim napakam ali izgubi berljivosti. Če so pravilno uporabljeni, so neprecenljivo bogastvo za srednje in napredne razvijalce.