📌 Questo è il punto di partenza del tutorial. Analizziamolo riga per riga.
.org 0 dice all'assemblatore: "il prossimo codice deve essere messo all'indirizzo 0 della memoria del microcontrollore".
All'indirizzo 0 c'è il vettore di reset: quando Arduino si accende o resetta, parte da qui.
rjmp init è un salto (relative jump) all'etichetta init. Serve per saltare la tabella dei vettori di interrupt che occupa i primi indirizzi.
Immagina che la memoria sia un lungo corridoio con tante porte. La porta 0 è l'ingresso principale. .org 0 dice: "mettiamo un cartello alla prima porta". rjmp init è come dire: "non entrare qui, vai direttamente alla porta numero 0x68, dove c'è la festa".
sbi 4,5 significa: vai al registro all'indirizzo 4 (che noi chiamiamo DDRB) e metti a 1 il bit numero 5.
Il bit 5 di DDRB corrisponde al pin fisico PB5, che su Arduino è il pin D13 (il LED integrato).
Mettere a 1 un bit in DDRB configura quel pin come OUTPUT. Può quindi mandare corrente (accendere LED) o essere spento.
✅ Solo il bit 5 è 1 → solo il pin D13 è in modalità OUTPUT. Gli altri pin sono in INPUT (alta impedenza).
Il cassetto 0x04 (DDRB) ha 8 interruttori. Ogni interruttore decide se il corrispondente pin può "parlare" (OUTPUT) o solo "ascoltare" (INPUT). Noi abbiamo attivato l'interruttore #5: ora il pin D13 è pronto per accendere il LED.
sbi 5,5 = registro 0x05 (PORTB), bit 5 = 1 → il pin D13 diventa HIGH (5V), il LED si accende.
cbi 5,5 = stesso registro, bit 5 = 0 → il pin D13 diventa LOW (0V), il LED si spegne.
Nota: se non avessimo prima impostato DDRB, queste istruzioni non avrebbero effetto (il pin sarebbe in INPUT).
⚡ Sul pin D13 ora c'è tensione! Il LED onboard si accende.
rcall è una chiamata a subroutine. Salva l'indirizzo di ritorno nello stack e salta all'etichetta wait1. Quando incontra ret, torna indietro.
ldi r17,32 carica il valore 32 nel registro r17. Questo sarà il loop esterno.
Poi si entra in wait:. Nota che r16 non viene inizializzato! La prima volta che il programma esegue dec r16, r16 vale 0 (i registri all'accensione sono 0).
dec r16 decrementa: 0 - 1 = 255 (perché gli 8 bit vanno da 0 a 255, sotto 0 si avvolge a 255). Poi continua 254, 253... fino a 0.
Quando r16 arriva a 0, brne wait non salta (perché zero = condizione falsa) e si passa a dec r17.
r17 viene decrementato: se non è zero, si torna a wait e r16 riparte da 0 (perché non l'abbiamo mai cambiato se non con dec). Quindi il ciclo interno si ripete 32 volte.
r16 è come una clessidra piccola che si rovescia 256 volte. Ogni volta che finisce, r17 è un contatore che tiene traccia di quante volte la clessidra piccola si è rovesciata. Quando r17 arriva a 0, è passato abbastanza tempo.
Dopo init: abbiamo la configurazione di DDRB (sbi 4,5). Poi accensione, pausa, spegnimento, pausa, e rjmp init.
Nota: rjmp init salta all'inizio, quindi riconfigura DDRB a ogni ciclo. Non serve, ma non fa male: impostare un bit già a 1 non cambia nulla. È solo un po' ridondante.
Il risultato: il LED lampeggia continuamente, con una pausa tra ON e OFF e una pausa tra OFF e ON.
Nel codice originale, r16 non viene mai inizializzato. Funziona perché la prima volta parte da 0, ma è una cattiva pratica: in programmi più complessi potrebbe contenere qualsiasi valore.
Ora il comportamento è deterministico e il codice è più robusto.
Ora che hai capito le basi, puoi esplorare altri progetti:
Sequenze di bit per far girare un motore 28BYJ-48 con ULN2003.
Vai al tutorial →Domanda: Cosa succede se ometto sbi 4,5 e faccio solo sbi 5,5?
Il LED non si accende. Perché sbi 4,5 configura il pin come OUTPUT. Se il pin è in INPUT, sbi 5,5 abilita la resistenza di pull-up interna, ma non manda corrente sufficiente ad accendere il LED (e comunque il pin non è in modalità output).
| Istruzione | Significato |
|---|---|
.org N |
Metti il codice successivo all'indirizzo N |
rjmp etichetta |
Salto relativo all'etichetta |
sbi indirizzo, bit |
Set Bit in I/O: metti a 1 il bit del registro |
cbi indirizzo, bit |
Clear Bit in I/O: metti a 0 il bit del registro |
rcall etichetta |
Chiama subroutine (salva indirizzo di ritorno) |
ret |
Ritorna dalla subroutine |
ldi reg, valore |
Carica valore immediato in un registro (r16-r31) |
dec reg |
Decrementa registro |
brne etichetta |
Branch if Not Equal (salta se l'ultima operazione non ha dato zero) |