Port Manipulation

Servus zusammen,

ich arbeite mich grad in das Thema ein, wie ich meinen CODE ein wenig zeit- und speicheroptimiert hinbekomme.

Die Manipulation mit bit-Math-Operationen der einzelnen Ports/Register und damit der Pins funzt soweit ganz gut.

Ich würde nun gerne in meinem Hauptprogramm auf eine Funktion mit 3 Variablen verweisen.
Variable 1: Pin (PD0...7 z.B.)
Variable 2: Pin (ebenfalls PD0...7)
Variable 3: Das Register (also hier PIND)

Die Pins zu übergeben ist kein Problem, die deklariere ich einfach z.B. in der Funktion als "int PD0".
Wie aber muss ich das Register deklarieren? Geht das überhaupt?

Für Antworten wäre ich dankbar.
Falls das gar nicht funktioniert wie ich mir das denke, bitte nicht schimpfen :smiley:

Klar geht das:

https://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

Erstmal vielen Dank.

Das sieht ein wenig wüst aus für jemanden, der nicht so häufig programmiert :smiley:

Ich wühl mich mal durch und melde mich zurück.
Vorher vielleicht noch ein, zwei Verständnisfragen:

Hinter den "Namen" PD0...7 stehen einfach Zahlenwerte richtig? Also einfach Werte von 0-7, die auf die entsprechenden bits 0-7 im entsprechenden Register verweisen? Daher auch die Deklaration als int. Byte, long oder sonst irgendwas numerisches würde als Datentyp auch laufen oder?

Was genau steht hinter dem "Namen" PIND? Steht da ein Wert hinter, der den momentanen Zustand der Pins beschreibt? Ist es eine Adresse im Speicher? Ich denke ich habe da noch ein paar Lücken, um richtig hinter den Kram zu steigen.

Das ist einfach die Bit-Nummer. Der Wert wird dann mit UND und ODER erzeugt. z.B. ein Bit setzen:

PORTB |= (1 << PB1);   //1 um 1 nach links schieben

1111 0000 | 0000 0010 = 1111 0010

Bit löschen:

PORTB &= ~(1 << PB7);  //1 um 7 nach links schieben

1111 0000 & ~(1000 0000) =
1111 0000 & 0111 1111 =
0111 0000

PINx und PORTx sind Adressen im Speicher. Da steckt die Adresse eines Special Function Registers dahinter (Stichwort: memory mapped I/O). Das ist ja der Grund weshalb man das als Zeiger übergeben muss.

Habe mir mal ein wenig zu Zeigern durchgelesen.
So wie ich das verstehe verweisen die auf eine Speicheradresse an der dann die Infos stehen, die zu übergeben ich versuche.

In meinem Fall: wenn ich per Zeiger auf die Speicheradresse von PINx/PORTx verweise, wird der an dieser Stelle im Speicher befindliche Wert übergeben, richtig?

make89:
In meinem Fall: wenn ich per Zeiger auf die Speicheradresse von PINx/PORTx verweise, wird der an dieser Stelle im Speicher befindliche Wert übergeben, richtig?

Nein, falsch.

Es wird die Adresse des Ports übergeben, damit man in den etwas schreiben oder aus dem etwas lesen kann.

Das volatile deutet an, dass sich der Wert an der Stelle des Ports jederzeit ändern kann, also ein Cachen nicht zulässig ist.

Ich würde nun gerne in meinem Hauptprogramm auf eine Funktion mit 3 Variablen verweisen.

Eine solche, oder ähnliche, Idee hatte ich auch mal....
Vielleicht kannst dir da ja was abschauen.

Beispiel im Thread:

#include "Pin.h"


// Standard LED auf dem UNO Board
#define LED PINDEF(B,5) //Pin13, PORTB Bit 5 

 

void setup() 
{
  setOutput(LED);
}

void loop() 
{
  togglePin(LED);
  delay(1000);
}

Mittlerweile bin ich ein Stück weiter und die neuere, vielleicht bessere, Variante haben wir eben hier besprochen.
Bzw. sind noch dabei.

Okay, heißt das, ich muss auf die Adresse verweisen und dann zusätzlich eine Anweisung zum Lesen der Infos an dieser Stelle geben?

Also zum Beispiel:

void setup() {
  
}

void loop() {
  test_function(PIND,PD2,PD3) //Funktionsaufruf mit Übergabe der Werte
}

void test_function(volatile int* port, int x,int y) {  //das Sternchen deklariert jetzt "port" als Pointer, richtig?
  int port_adresse = &port;  // port_adresse wird die Speicheradresse von port zugewiesen
  int port_wert = *port_adresse; //port_wert wird der Wert an der Adresse zugewiesen
  
  ....hier wird dann irgendwas cooles mit port_wert,x und y gemacht
}


Ich versuche noch durch das Prinzip zu steigen...

Ich glaube ich habe es hinbekommen:

void loop() {
  while ((PIND & (1 << PD2))) {}
  tempmicros=micros();
  while ((PIND & (1 << PD2)) == 0) {}
  if ((micros()-tempmicros)>500) { //if the HIGH pulse was longer than 500 micros we are at the start of a new bit sequence
    decode(PD2,PD3,&PIND); //decode the bit sequence
  }
}



void decode(int x,int y,volatile uint8_t *port) {
  sign=1;
  value=0;
  for (i=0;i<23;i++) {
    while ((*port & (1 << x))) {}
    while ((*port & (1 << x)) == 0) {}
    if ((*port & (1 << y)) == 0) {    
      if (i<20) {
        value|= 1<<i;
      }
      if (i==20) {
        sign=-1;
      }
    }
  }
  result=(value*sign);
  Serial.println(result); //print result
}

Der Code macht was er soll.
Nun noch der Versuch genau zu verstehen was passiert:

Indem ich die Übergabevariable *port mit dem Sternchen als Pointer deklariere, wird eine Adresse als Übergabewert erwartet --> diese übergebe ich mit &PIND (Adresse des Registers, an der dann die Status der Pins im Port D gespeichers sind).
Die Adresse wird in der Funktion mit "*port" wieder dereferenziert, es wird also der Inhalt an der Position im Speicher gelesen und damit weiter gerechnet.

Ich hoffe, ich hab das Ganze durchdrungen :slight_smile:

Korrekt

Super, vielen Dank :slight_smile: