Comunicația serială permite schimbul de date între microcontroler și calculator, senzori inteligenți sau alte plăci. Este cea mai folosită interfață din practică - și singura prin care puteți vedea ce se întâmplă în interiorul unui sistem fără ecran.
1Obiectivele lucrării
- Înțelegerea structurii cadrului asincron: start, date, paritate, stop
- Calculul registrului
UBRRși al erorii de baud rate - Configurarea modulului USART prin regiștri
- Compararea standardelor TTL, RS-232 și RS-485
- Recepția și interpretarea comenzilor primite de la calculator
- Implementarea recepției prin întrerupere
2Materiale necesare
- 2 Plăci Arduino Uno
- 1 Breadboard
- 1 LED + rezistor 220 Ω
- 4 Fire jumper
- 1 Modul MAX485 (opțional)
3Sincron vs. asincron
În comunicația sincronă (SPI, I²C), un fir separat transportă semnalul de ceas care marchează momentul citirii fiecărui bit. În comunicația asincronă (UART) nu există fir de ceas: ambele dispozitive trebuie configurate la aceeași viteză, iar sincronizarea se reface la fiecare cadru, pornind de la frontul bitului de start.
4Structura cadrului
| Element | Lungime | Valoare | Rol |
|---|---|---|---|
| Linie în repaus | - | 1 (HIGH) | indică lipsa transmisiei |
| Bit de start | 1 bit | întotdeauna 0 | frontul descendent sincronizează receptorul |
| Biți de date | 5–9 (uzual 8) | date utile | transmiși cu LSB primul |
| Bit de paritate | 0 sau 1 bit | pară / impară | detectarea erorilor simple |
| Bit(i) de stop | 1 sau 2 biți | întotdeauna 1 | marchează sfârșitul și separă cadrele |
5Constructor de cadru UART
Scrieți un caracter și urmăriți cum se construiește cadrul pe fir: bitul de start, cei opt biți de date transmiși cu LSB primul, paritatea opțională și bitul de stop. Schimbați viteza și observați cum se modifică durata fiecărui bit și eroarea de baud.
6Calculul vitezei de transmisie
| Baud dorit | UBRR (16 MHz) | Baud real | Eroare | Verdict |
|---|---|---|---|---|
| 2 400 | 416 | 2 398 | −0,08 % | excelent |
| 9 600 | 103 | 9 615 | +0,16 % | excelent |
| 19 200 | 51 | 19 231 | +0,16 % | excelent |
| 38 400 | 25 | 38 462 | +0,16 % | bun |
| 57 600 | 16 | 58 824 | +2,12 % | ⚠ la limită |
| 115 200 | 8 | 111 111 | −3,55 % | ⚠ nesigur |
U2X0 din UCSR0A înjumătățește
divizorul și reduce eroarea la 115200 baud de la −3,55% la +2,12%.7Regiștrii USART
Configurația implicită de mai jos este cea standard „8N1": 8 biți de date, fără paritate, un bit de stop, recepție și transmisie activate.
| Bit | Registru | Rol |
|---|---|---|
RXC0 | UCSR0A | 1 = a sosit un octet complet, gata de citit |
UDRE0 | UCSR0A | 1 = registrul de transmisie este liber |
U2X0 | UCSR0A | dublează viteza; reduce eroarea la baud mari |
RXCIE0 | UCSR0B | activează întreruperea la recepție |
RXEN0 / TXEN0 | UCSR0B | pornesc receptorul, respectiv emițătorul |
UPM01:UPM00 | UCSR0C | 00 = fără paritate · 10 = pară · 11 = impară |
USBS0 | UCSR0C | 0 = un bit de stop · 1 = doi biți |
UCSZ01:UCSZ00 | UCSR0C | 11 = 8 biți de date |
8Standarde electrice
| Standard | Niveluri | Distanță max. | Topologie | Utilizare |
|---|---|---|---|---|
| TTL / UART | 0 V / 5 V | ~1 m | punct la punct | între plăci, pe același cablaj |
| RS-232 | ±3…±15 V, logică inversată | ~15 m | punct la punct | aparate de măsură, echipamente industriale vechi |
| RS-485 | diferențial ±1,5…5 V | ~1200 m | multipunct, până la 32 noduri | industrial, Modbus, automatizări |
9UART, SPI și I²C - comparație
| Criteriu | UART | SPI | I²C |
|---|---|---|---|
| Fire necesare | 2 (+ masă) | 4 | 2 |
| Ceas | nu (asincron) | da | da |
| Viteză tipică | 9,6–115 kbit/s | până la 8 Mbit/s | 100–400 kbit/s |
| Număr dispozitive | 2 | multe (câte un pin CS fiecare) | până la 127 (adresare) |
| Duplex | full duplex | full duplex | half duplex |
| Pini pe Uno | D0, D1 | D10–D13 | A4 (SDA), A5 (SCL) |
| Utilizare tipică | PC, module GPS/GSM | carduri SD, display-uri, nRF24 | senzori, EEPROM, RTC |
10Schema de montaj
SoftwareSerial pe alți pini.11Simulator: parser de comenzi
Datele sosesc caracter cu caracter, nu ca șiruri complete. Urmăriți cum se acumulează caracterele într-un buffer până la primirea terminatorului de linie, moment în care comanda devine completă și poate fi interpretată.
if (Serial.available()) urmat de o
singură citire prelucrează doar un caracter per iterație a lui loop(). Dacă bucla conține
un delay(), bufferul de 64 de octeți se umple și datele se pierd. Folosiți
while, nu if.12Cod sursă
Interpretor de comenzi
const uint8_t PIN_LED = 13;
String comanda = "";
void setup() {
Serial.begin(9600);
pinMode(PIN_LED, OUTPUT);
Serial.println("Comenzi: ON, OFF, STARE, PWM <0-255>");
}
void loop() {
while (Serial.available() > 0) { // WHILE, nu IF!
char c = Serial.read();
if (c == '\n' || c == '\r') {
comanda.trim();
comanda.toUpperCase();
if (comanda == "ON") {
digitalWrite(PIN_LED, HIGH);
Serial.println("LED aprins");
} else if (comanda == "OFF") {
digitalWrite(PIN_LED, LOW);
Serial.println("LED stins");
} else if (comanda == "STARE") {
Serial.print("LED este ");
Serial.println(digitalRead(PIN_LED) ? "aprins" : "stins");
} else if (comanda.startsWith("PWM ")) {
int val = comanda.substring(4).toInt();
val = constrain(val, 0, 255);
analogWrite(9, val);
Serial.print("PWM setat la "); Serial.println(val);
} else if (comanda.length() > 0) {
Serial.print("Comanda necunoscuta: ");
Serial.println(comanda);
}
comanda = "";
} else {
comanda += c;
}
}
}
Configurare directă a modulului USART
#define F_CPU 16000000UL
#define BAUD 9600
#define UBRR_VAL ((F_CPU / (16UL * BAUD)) - 1)
void usartInit(uint16_t ubrr) {
UBRR0H = (uint8_t)(ubrr >> 8); // partea inalta a divizorului
UBRR0L = (uint8_t)ubrr; // partea joasa
UCSR0B = (1 << RXEN0) | (1 << TXEN0); // activeaza receptia si transmisia
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // format 8N1
}
void usartTransmite(uint8_t data) {
while (!(UCSR0A & (1 << UDRE0))); // asteapta golirea registrului
UDR0 = data;
}
uint8_t usartReceptioneaza(void) {
while (!(UCSR0A & (1 << RXC0))); // asteapta un octet complet
return UDR0;
}
void usartScrieText(const char *s) {
while (*s) usartTransmite(*s++);
}
void setup() {
usartInit(UBRR_VAL);
usartScrieText("USART initializat\r\n");
}
void loop() {
uint8_t c = usartReceptioneaza();
usartTransmite(c); // ecou inapoi
}
Recepție prin întrerupere
Așteptarea activă blochează programul. Recepția pe întrerupere eliberează complet bucla principală:
volatile char bufer[32];
volatile uint8_t index = 0;
volatile bool mesajComplet = false;
ISR(USART_RX_vect) { // se executa la fiecare octet primit
char c = UDR0; // citirea sterge automat fanionul RXC0
if (c == '\n' || index >= 31) {
bufer[index] = '\0';
index = 0;
mesajComplet = true;
} else {
bufer[index++] = c;
}
}
void setup() {
Serial.begin(9600);
UCSR0B |= (1 << RXCIE0); // activeaza intreruperea la receptie
sei();
Serial.println("Gata de comenzi");
}
void loop() {
if (mesajComplet) {
mesajComplet = false;
Serial.print("Am primit: ");
Serial.println((char*)bufer);
}
// bucla ramane complet libera pentru alte sarcini
}
Transmiterea datelor între două plăci
const uint8_t PIN_BUTON = 2;
void setup() {
Serial.begin(9600);
pinMode(PIN_BUTON, INPUT_PULLUP);
}
void loop() {
static bool ultimaStare = HIGH;
bool stare = digitalRead(PIN_BUTON);
if (stare != ultimaStare) {
ultimaStare = stare;
Serial.print("B:"); // prefix pentru identificarea mesajului
Serial.println(stare == LOW ? 1 : 0);
delay(50); // debounce simplu
}
}
const uint8_t PIN_LED = 13;
String linie = "";
void setup() {
Serial.begin(9600);
pinMode(PIN_LED, OUTPUT);
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
linie.trim();
if (linie.startsWith("B:")) {
int val = linie.substring(2).toInt();
digitalWrite(PIN_LED, val);
}
linie = "";
} else {
linie += c;
}
}
}
13Montaj interactiv
Monitorul serial din montajele de mai jos este bidirecțional: microcontrolerul scrie în el, iar dumneavoastră puteți scrie înapoi, în câmpul de sub monitor. Este exact fluxul pe care îl veți avea în laborator, prin cablul USB.
1 în câmpul de sub monitor și apăsați Enter: LED-ul se aprinde. Priviți bitul RXC0 din UCSR0A - devine 1 în clipa în care sosește un caracter și se șterge singur când programul citește UDR0. Toată comunicația serială se reduce la aceste două semnale de stare.readStringUntil('\n') citește până la Enter, iar trim() scoate spațiile și caracterul de capăt de linie - fără el, comparația cu "LED ON" eșuează fără motiv aparent.14Sarcini de lucru
- Realizați un program care răspunde la comenzile ON, OFF și STARE primite pe serial.
- Adăugați comanda
PWM <valoare>care reglează intensitatea unui LED. - Calculați UBRR pentru 4800, 9600 și 115200 baud și verificați erorile rezultate.
- Setați în program 9600 baud și în monitorul serial 19200 - observați ce caractere apar și explicați de ce.
- Conectați două plăci prin TX–RX și transmiteți starea unui buton de la una la cealaltă.
- Măsurați durata transmiterii a 100 de caractere și comparați cu valoarea teoretică.
- Implementați recepția prin întrerupere și verificați că bucla principală rămâne responsivă chiar cu
delay(500). - Înlocuiți
whilecuifîn bucla de citire și observați pierderea caracterelor.
15Aplicație de aprofundare
<ADRESA:COMANDA:VALOARE:CRC>, de exemplu <01:PWM:180:A3>.
Implementați: un analizor care extrage cele patru câmpuri, verificarea adresei (placa răspunde doar la
adresa proprie), calculul unei sume de control simple (XOR pe toate caracterele) și respingerea cadrelor
corupte.
Testați robustețea introducând intenționat erori în cadru și verificați că acestea sunt detectate. Extindere suplimentară: legați trei plăci pe aceeași magistrală prin module MAX485 și verificați adresarea selectivă.