Fehlermeldung kopf

Arduino Portmanipulation
Tipps zu Portmanipulation am Arduino

Ports am Arduino werden üblicherweise mit "digitalWrite()" beschrieben. Dieser Befehl benötigt jedoch einiges an Zeit, manchmal muss es schneller gehen. Dann sind direkte Portmanipulationen gefragt.


In obigem Bild kann man ablesen, welcher Arduino-Port zu welchem Bit und 328-Port gehört. Der ATMega328 hat drei Ports: PORTB, PORTC und PORTD. Der Arduino-Port 16 (violett) ist z.B. PC2 (gelb), was bedeutet, daß er das Bit 2 des 328-Ports PORTC ist. Für jeden digitalWrite()-Befehl gibt es eine direkte Entsprechung. "digitalWrite(16,HIGH)" kann z.B. durch "PORTC |= (1‹‹PC2)" ersetzt werden, "digitalWrite(16,LOW)" entsprechend durch "PORTC &= ~(1‹‹PC2)". Diese Schreibweise ist weniger einprägsam, dafür geht die Ausführung schneller.
Die Faustregel:
(1) In obigem Bild ermitteln, welchem Bit in welchem 328-Port der Arduino-Port entspricht.
(Arduino-Port 10 entspricht z.B. PB2 also Bit 2 des 328-Ports B.)
(2) Für HIGH den direkten Befehl PORTX |= (1‹‹PX#) verwenden. X ist dabei entweder B, C oder D und # ist die Bitnummer.
(Arduino-Port 10 HIGH entspricht somit: PORTB |= (1‹‹PB2)
(3) Für LOW den direkten Befehl PORTX &= ~(1‹‹PX#) verwenden. X ist dabei entweder B, C oder D und # ist die Bitnummer.
(Arduino-Port 10 LOW entspricht somit: PORTB &= ~(1‹‹PB2)
Noch ein Beispiel:
"digitalWrite(7,HIGH)" kann durch "PORTD |= (1‹‹PD7)" ersetzt werden.
"digitalWrite(7,LOW)" kann durch "PORTD &= ~(1‹‹PD7)" ersetzt werden.
Da der Arduino-Port 7 (violett) dem 328-Port PD7 (gelb) entspricht.



Anstatt einzelner Bits lassen sich auch mehrere Bits eines Ports gleichzeitig setzen. Dies ist z.B. günstig, wenn man eine Anzeige multiplexen möchte. In folgendem Beispiel sollen sechs 7-Segmentanzeigen angesteuert werden, vier für Zahlenwerte und zwei für Einheiten.
volatile byte tcnt2;
volatile byte mpx;
byte data[6]; //mpx-Daten
byte z[17]; //7-Segment

void setup(){
//Output für die Segmente
for(byte i=0;i<8;i++){
pinMode(i,OUTPUT);
}
//Output für die Stellen
for(byte i=8;i<14;i++){
pinMode(i,OUTPUT);
}
// Timer2 auf 1ms setzen
TIMSK2 &= ~(1‹‹TOIE2);
TCCR2A &= ~((1‹‹WGM21) | (1‹‹WGM20));
TCCR2B &= ~(1‹‹WGM22);
TIMSK2 &= ~(1‹‹OCIE2A);
TCCR2B |= (1‹‹CS22) | (1‹‹CS20);
TCCR2B &= ~(1‹‹CS21);
tcnt2 = 131; // = 6 für 2ms
TCNT2 = tcnt2;
TIMSK2 |= (1‹‹TOIE2);

//7-Segment-Definitionen
//0 bedeutet Segment an
z[0]=B11000000; //Ziffern
z[1]=B11111001;
z[2]=B10100100;
z[3]=B10110000;
z[4]=B10011001;
z[5]=B10010010;
z[6]=B10000010;
z[7]=B11111000;
z[8]=B10000000;
z[9]=B10010000; //Ziffern
z[10]=B11111111; //Blank
z[11]=B10011100; //Proz_Grad
z[12]=B11000110; //C
z[13]=B10001011; //h
z[14]=B10001100; //P
z[15]=B10100011; //Proz_unten
z[16]=B10111111; //Minus
//Anfangswerte setzen (beliebig)
data[0]=z[10];
data[1]=B11000001;
data[2]=B11000111;
data[3]=B11111001;
data[4]=z[10];
data[5]=z[10];
delay(2000);
}

//Interrupt
ISR(TIMER2_OVF_vect) {
TCNT2 = tcnt2;
// multiplex der anzeige
mpx++;
if(mpx>5) mpx=0;
PORTB |= B00111111; //alle aus, d.h.auf HIGH
PORTD |= B01111111; //alle aus, d.h.auf HIGH
PORTD &= data[mpx]; //abc... der 7 Segment
digitalWrite(8+mpx,LOW); //LOW ist aktiv
}

void loop(){
//Anzeigewerte in data[] bereitstellen
//z.B. zur Anzeige der Temperatur
byte temperatur
//Hier Wert für "temperatur" ermitteln
//Anzeige
data[0]=z[10]; //blank
data[1]=z[10];
if(temperatur<0) data[1]=z[16]; //Minus
data[2]=z[temperatur/10];
data[3]=z[temperatur%10];
data[4]=z[11]; //Grad Celsius
data[5]=z[12];
//weitere Anzeigewerte ermitteln...
//z.B. für Feuchte
byte feuchte;
//Hier Wert für "feuchte" ermitteln
//Anzeige
data[0]=z[10]; //blank
data[1]=z[10];
data[2]=z[feuchte/10];
data[3]=z[feuchte%10];
data[4]=z[11]; //Prozent
data[5]=z[15];
//weitere Anzeigewerte ermitteln...
//z.B. für Druck
int druck;
//Hier Wert für "druck" ermitteln
//Anzeige
if(druck<1000){
data[0]=z[10];
}
else{
data[0]=z[1];
druck=druck-1000;
}
data[1]=z[druck/100];
druck=druck%100;
data[2]=z[druck/10];
data[3]=z[druck%10];
data[4]=z[13]; //hP
data[5]=z[14];
}
Hardwareseitig sind die Segmente der 7-Segmentanzeigen alle parallel (über Widerstände) an den Port D angeschlossen, das sind die Arduino-Ports 0 bis 7. Die einzelnen 7-Segmentanzeigen werden über PNP-Transistoren eingeschaltet, wobei die PNP-Transistoren über den Port B gemultiplext werden, das sind die Arduino-Ports 8 bis 13.

Natürlich lassen sich auch Eingaben oder Portdefinitionen durch direkte Portmanipulation erledigen, diese werden jedoch weniger häufig ein Zeitproblem darstellen, weshalb hier auf einschlägige Internetseiten verwiesen wird, z.B. hier.
(In den Texten stiftet der Begriff "Port" manchmal Verwirrung: In der Arduino-IDE steht "Port" für einen Ein-/Ausgang, also für ein Bit. Bei den Portmanipulationen steht "Port" aber auch für die Register des ATMega328 (PORTB, PORTC, PORTD), also für ein Byte.)