Prima ședință fixează bazele: cum arată un program care rulează la nesfârșit pe un microcontroler, ce resurse are efectiv ATmega328P, ce face fiecare pin al plăcii și cum se comandă prima ieșire digitală.
1Obiectivele lucrării
- Instalarea mediului Arduino IDE și configurarea plăcii și a portului de încărcare
- Înțelegerea structurii
setup()/loop()și a motivului pentru care există - Identificarea funcțiilor fiecărui pin al plăcii Arduino Uno
- Alegerea tipurilor de date potrivite pentru un sistem cu 2 KB de RAM
- Comanda unui pin de ieșire și verificarea funcționării prin LED
2Materiale necesare
- 1 Placă Arduino Uno (ATmega328P)
- 1 Cablu USB tip B
- 1 Breadboard
- 1 LED de 5 mm
- 1 Rezistor 220 Ω
- 2 Fire jumper
3Arhitectura plăcii
Placa Arduino Uno este construită în jurul microcontrolerului ATmega328P, un procesor pe 8 biți cu arhitectură Harvard modificată. Resursele lui sunt modeste - și tocmai de aceea fiecare decizie de programare contează.
| Resursă | Valoare | Observații |
|---|---|---|
| Frecvență de ceas | 16 MHz | cristal extern; un ciclu durează 62,5 ns |
| Memorie Flash (program) | 32 KB | din care 0,5 KB ocupați de bootloader |
| Memorie SRAM (date) | 2 KB | aici trăiesc variabilele - resursa cea mai critică |
| Memorie EEPROM | 1 KB | păstrează datele după oprirea alimentării |
| Pini digitali | 14 (din care 6 cu PWM) | D0–D13 |
| Intrări analogice | 6 | A0–A5, convertor pe 10 biți |
| Curent maxim pe pin | 40 mA | absolut maxim; recomandat sub 20 mA |
| Curent total pe toți pinii | 200 mA | limita microcontrolerului |
int nu încape deloc.
La microcontrolere, alegerea tipului de date nu este o chestiune de stil, ci de funcționare.4Explorator de pini
Apăsați pe orice pin pentru a vedea ce funcții are. Observați că mulți pini au funcții multiple - de exemplu D13 este simultan ceasul SPI și LED-ul de pe placă, iar D0 și D1 sunt ocupate de comunicația serială.
D0 și D1 sunt folosiți de
Serial și de încărcarea programului. Dacă legați ceva la ei, încărcarea sketch-ului poate
eșua. D13 are un LED și un rezistor deja montate pe placă, deci nu este ideal ca intrare.
5Structura programului
Un program obișnuit pornește, își face treaba și se termină. Un program pentru microcontroler nu se termină niciodată: cât timp placa este alimentată, procesorul trebuie să execute ceva. Mediul Arduino impune de aceea două funcții:
| Funcție | Când se execută | Rol tipic |
|---|---|---|
setup() | o singură dată, la pornire sau după reset | configurarea pinilor, pornirea comunicației, inițializarea perifericelor |
loop() | continuu, imediat ce setup() s-a terminat | citirea senzorilor, decizii, comanda ieșirilor |
/* Cele doua functii pe care le scrieti dumneavoastra in schita: */
void setup() { /* codul VOSTRU de initializare */ }
void loop() { /* codul VOSTRU, repetat la nesfarsit */ }
/* ...iar mediul Arduino adauga singur, in spate, exact atat: */
int main(void)
{
init(); // configureaza timerele si ADC-ul, apelata automat
setup(); // o singura data, la pornire
for (;;) { // bucla infinita
loop(); // la nesfarsit
}
return 0; // nu se ajunge niciodata aici
}
loop(),
restul programului nu se mai execută niciodată. Iar dacă loop() este gol, procesorul
tot rulează - consumând curent degeaba.6Simulator de execuție
Urmăriți pas cu pas ordinea reală a execuției: setup() rulează o
singură dată, apoi loop() se reia la infinit. Observați valoarea contorului static,
care demonstrează că funcția chiar este apelată repetat.
7Tipuri de date pe AVR
| Tip | Octeți | Domeniu | Când se folosește |
|---|---|---|---|
bool | 1 | true / false | indicatori; ocupă tot un octet, nu un bit |
char | 1 | −128 … 127 | caractere ASCII |
uint8_t / byte | 1 | 0 … 255 | numere de pini, contoare mici, valori de registru |
int | 2 | −32 768 … 32 767 | atenție: pe AVR are 16 biți, nu 32! |
unsigned int | 2 | 0 … 65 535 | valori ADC, contoare medii |
long | 4 | ±2,1 · 10⁹ | millis(), micros() |
float | 4 | ±3,4 · 10³⁸, ~7 cifre | evitați: nu există unitate hardware, calculele sunt lente |
int t = 1000 * 60; dă rezultat greșit, pentru că 60 000 depășește domeniul lui
int pe 16 biți. Corect: long t = 1000L * 60; - sufixul L
forțează calculul pe 32 de biți.float durează de zeci de ori mai mult decât una întreagă și ocupă
aproape 1 KB de Flash doar pentru bibliotecă. Acolo unde este posibil, lucrați cu întregi scalați
(de exemplu temperatura în zecimi de grad, ca int).Calificatori importanți
| Calificator | Efect | Când este obligatoriu |
|---|---|---|
const | valoare care nu se modifică | numere de pini, praguri |
static | păstrează valoarea între apeluri | contoare locale în loop() |
volatile | interzice optimizarea accesului | orice variabilă modificată într-un ISR |
PROGMEM | stochează datele în Flash, nu în RAM | tabele constante mari, texte fixe |
8Schema de montaj
9Cod sursă
Programul minimal - Blink
const uint8_t LED_PIN = 8; // const + uint8_t: nu ocupa RAM inutil
void setup() {
pinMode(LED_PIN, OUTPUT); // pinul devine iesire
}
void loop() {
digitalWrite(LED_PIN, HIGH); // aprinde
delay(500); // asteapta 500 ms
digitalWrite(LED_PIN, LOW); // stinge
delay(500);
}
Aceeași funcționalitate, la nivel de registru
Funcțiile pinMode() și digitalWrite() există doar în mediul Arduino.
Aceleași operații scrise direct pe regiștri funcționează pe orice compilator AVR și se execută de
aproximativ 50 de ori mai rapid:
void setup() {
DDRB |= (1 << PB0); // pinul 8 devine iesire (echiv. pinMode(8, OUTPUT))
}
void loop() {
PORTB |= (1 << PB0); // pinul 8 pe HIGH (echiv. digitalWrite(8, HIGH))
delay(500);
PORTB &= ~(1 << PB0); // pinul 8 pe LOW (echiv. digitalWrite(8, LOW))
delay(500);
}
| Operație | Funcție Arduino | Echivalent pe registru |
|---|---|---|
| Pin ca ieșire | pinMode(8, OUTPUT) | DDRB |= (1 << PB0); |
| Pin ca intrare | pinMode(2, INPUT) | DDRD &= ~(1 << PD2); |
| Intrare cu pull-up | pinMode(2, INPUT_PULLUP) | DDRD &= ~(1<<PD2); PORTD |= (1<<PD2); |
| Scrie HIGH | digitalWrite(8, HIGH) | PORTB |= (1 << PB0); |
| Scrie LOW | digitalWrite(8, LOW) | PORTB &= ~(1 << PB0); |
| Citește pin | digitalRead(2) | (PIND & (1 << PD2)) |
(1 << PB0) se numește
mască de biți și este fundamentul întregului curs. Dacă nu vă este încă limpede de ce
funcționează, parcurgeți Laboratorul 1B înainte de a trece
la lucrarea următoare - acolo este explicată pas cu pas, cu simulator interactiv.Depanare fără ecran: LED-ul ca instrument
Microcontrolerul nu are ecran. Până la lucrarea a șasea, când veți învăța comunicația serială și veți putea trimite text către calculator, singurul instrument de depanare pe care îl aveți este chiar LED-ul: îl folosiți ca să arătați că programul a ajuns într-un anumit punct sau ca să „afișați” un număr mic, prin clipiri numărate.
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
static uint8_t contor = 0; // static: isi pastreaza valoarea intre apeluri
contor++;
// "afisam" contorul: cate o clipire pentru fiecare unitate
for (uint8_t i = 0; i < contor; i++) {
digitalWrite(13, HIGH);
delay(150);
digitalWrite(13, LOW);
delay(250);
}
delay(1500); // pauza lunga = s-a terminat "afisarea"
if (contor >= 5) contor = 0; // luam de la capat
}
loop() vedeți cu o
clipire mai mult: 1, 2, 3, 4, 5, apoi iar 1. Aceasta este dovada că static chiar
păstrează valoarea între apeluri - fără el, contorul ar reveni la 0 de fiecare dată și ați vedea
mereu o singură clipire.Verificarea memoriei disponibile
Dimensiunile tipurilor le găsiți în tabelul de la capitolul 7. Cât RAM a mai rămas liber se poate afla însă doar în timpul execuției, cu funcția de mai jos. Neavând încă monitorul serial, programul nu tipărește valoarea, ci o semnalizează: dacă memoria liberă scade sub un prag, LED-ul rămâne aprins continuu.
// Returneaza numarul de octeti liberi intre heap si stiva
int memorieLibera() {
extern int __heap_start, *__brkval;
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
if (memorieLibera() < 256) {
digitalWrite(13, HIGH); // alarma: memorie aproape epuizata
} else {
digitalWrite(13, LOW);
}
delay(500);
}
10Montaj interactiv
Montajele de mai jos rulează codul chiar în pagină, pe un ATmega328P simulat. Nu este o animație: PORTB chiar comandă pinul, LED-ul se aprinde pentru că bitul 5 a devenit 1, iar ceasul de 16 MHz curge la fel ca pe placa reală. Apăsați Pornește, apoi schimbați codul și porniți din nou.
pinMode(13, OUTPUT) pune pe 1 bitul DDB5, iar fiecare digitalWrite comută bitul PORTB5. Funcțiile Arduino nu fac nimic magic - scriu în aceiași regiștri pe care îi veți folosi direct la lucrarea următoare. Încercați delay(100) și priviți cum se îndesesc impulsurile pe osciloscop.11Sarcini de lucru
- Instalați Arduino IDE, selectați placa Arduino Uno și portul serial corect.
- Încărcați programul Blink și verificați funcționarea LED-ului de pe placă (D13).
- Montați un LED extern cu rezistor de 220 Ω pe pinul D8 și modificați programul.
- Calculați curentul prin LED și verificați că se încadrează sub 20 mA.
- Modificați intervalele astfel încât LED-ul să fie aprins 100 ms și stins 900 ms.
- Rulați programul de verificare a dimensiunilor și notați valorile pentru
intșilong. - Verificați experimental capcana
int t = 1000 * 60;și corectați-o cu sufixulL. - Afișați memoria RAM liberă și observați cum scade după declararea unui tablou de 200 de
int.
12Aplicație de aprofundare
Întrebare de reflecție: ce tip de date este potrivit pentru variabila care ține pauza și de ce un
uint8_t nu ar fi suficient?