Care este punctul de plecare?
Hai sa luam un program simplu: blink.ino.
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
Sketch uses 928 bytes (2%) of program storage space. Max is 32,256 bytes.
Cum optimizez programul?
Vrem sa eliminam folosirea instructiunii digitalWrite() (si pinMode()).
Exista avantaje reale pentru folosirea lor: sunt usor de folosit, sunt portabile la alte microcontrollere din aceiasi familiei (daca vrem sa mutam programul de pe un Arduino Uno pe un Mega, de exemplu) si au implementata o metoda de a ne asigura ca nu scriem valori gresite. Dar, ca efect secundar, folosirea instructiunii digitalWrite() face codul mai lung si il incetineste. Cu cat? Vedem imediat!
Microcontrollerul pentru platforma Arduino este ATmega328p (acelasi pinout ca si ATmega168).
Pinul 13 apartine portului B al microcontrollerului – PB5.
Fiecare port (B, C, D) are asociati cate trei registrii:
- DDRx (DDRB, DDRC etc);
- PORTx (PORTx etc);
- PINx (PINB etc).
DDRx este registrul in care se defineste directia portului/pinului (Data Direction):
- 0 inseamna INPUT;
- 1 inseamna OUTPUT.
PORTx este registrul in care se scrie valoarea pinului (cand este declarat ca OUTPUT):
PINx – contine valoarea pinilor portului (atunci cand pinul este declarat ca INPUT):
Astfel, pentru a declara pinul 13 ca OUTPUT, putem scrie:
DDRB = DDRB | (0x01 << 5); //sau DDRB |= (0x01 << 5)
- Cateva explicatii:
- dorim sa afectam doar pinul 5 al portului B, iar celeilalti pini sa ramana nemodificati;
- tabela de adevar pentru operatorul logic OR este asa: daca ambii operanzi sunt egali cu 0, atunci rezultatul este 0; in rest, pentru orice alta combinatie (0 OR 1, 1 OR 1) rezultatul este 1. Sau, altfel spus, daca un operand este 0, atunci facand operatia cu OR ne va da valoarea celui de-al doilea operand (adica va lasa nemodificata valoarea celui de-al doilea operand). Daca, insa, operandul este 1, atunci operatia cu OR va da valoarea 1 indiferent de valoarea celui de-al doilea operand;
- operatorul “<<” reprezinta shiftare bit cu un numar de pozitii la stanga;
- astfel DDRB = DDRB | (0x01 << 5) este echivalenta cu DDRB = DDRB | 00100000. Aceasta inseamna ca doar bitul de pe pozitia 5 (unde bitul cel mai din dreapta este bitul de pe pozitia 0, iar bitul cel mai din stanga este bitul de pe pozitia 7) va primi valoarea 1, iar restul bitilor vor avea valoarea nemodificata.
In acelasi mod, ca sa inlocuim digitalWrite() cu o varianta mai “ieftina”:
PORTB |= (0x01 << 5); //pentru a aprinde LED
PORTB &= ~(0x01 << 5); //pentru a stinge LED
Observatii:
- prima instructiune este la fel ca cea explicata mai sus. Doar registrul difera – PORTB in loc de DDRB. PORTB = PORTB | 00100000. Aceasta inseamna ca doar bitul de pe pozitia 5 (unde bitul cel mai din dreapta este bitul de pe pozitia 0, iar bitul cel mai din stanga este bitul de pe pozitia 7) va primi valoarea 1, iar restul bitilor vor avea valoarea nemodificata;
- tabela de adevar pentru operatorul logic AND este asa: daca unul din operanzi este egal cu 0, atunci rezultatul este 0; daca ambii operanzi sunt 1, rezultatul este 1. Sau, altfel spus, daca un operand este 1, atunci facand operatia cu AND ne va da valoarea celui de-al doilea operand (adica va lasa nemodificata valoarea celui de-al doilea operand). Daca, insa, operandul este 0, atunci operatia cu AND va da valoarea 0 indiferent de valoarea celui de-al doilea operand;
- operatorul “~” inseamna negare bit (0 trece in 1, iar 1 trece in 0);
- astfel PORTB &= ~(0x01 << 5) este echivalenta cu PORTB = PORTB & ~(00100000) sau PORTB = PORTB & 11011111. Aceasta inseamna ca doar bitul de pe pozitia 5 (unde bitul cel mai din dreapta este bitul de pe pozitia 0, iar bitul cel mai din stanga este bitul de pe pozitia 7) va primi valoarea 0, iar restul bitilor vor avea valoarea nemodificata.
Daca rescriem programul cu instructiunile de mai sus, avem urmatoarul program:
void setup(){
DDRB |= (0x01 << 5) ;//output
}
void loop(){
PORTB |= (0x01 << 5);
delay(1000);
PORTB &= ~(0x01 << 5);
delay(1000);
}
Sketch uses 644 bytes (1%) of program storage space. Max is 32,256 bytes.
Vedem o reducere a dimensiunii programului cu aprox 30% (de la 928 bytes la 644 bytes).
Exista imbunatatiri din punct de vedere al vitezei?
void setup() {
pinMode(13, OUTPUT);
Serial.begin(115200);
}
void loop() {
Serial.println(micros());
for(long i = 0; i < 500000; i++){
digitalWrite(13, HIGH);
digitalWrite(13, LOW);
}
Serial.println(micros());
}
Timpul intre doua treceri prin loop() este de 3.553.196 microsecunde. |
void setup() {
pinMode(13, OUTPUT);
Serial.begin(115200);
}
void loop() {
Serial.println(micros());
for(long i = 0; i < 500000; i++){
PORTB |= (1 << 5);
PORTB &= ~(1 << 5);
}
Serial.println(micros());
}
Timpul intre doua treceri prin loop() este de 314.808 microsecunde. |
Obtinem o imbunatatire a vitezei de mai mult de 10 ori!