Verständnis frage --> pinMode

Wieso übergibst nicht einfach den den Port und die Pin Nummer als Parameter?

So wie du das machen willst, verlierst du nämlich den eigentlich Sinn von Funktionen. Die Wiederverwertbarkeit von Code

Allgemeiner Test Code für die LED an Pin 13 (PB5):

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  blink(&PORTB, PB5);
  delay(500);
}

void blink(volatile byte* port, byte pin)
{
  *port ^= _BV(pin);   //Pin mit XOR invertieren
}

Könnte bei dir dann so aussehen:

float schieber(volatile byte* port, byte pin1, byte pin2)
{
}

Siehe auch hier:
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

Das erzeugt aber auch etwas Overhead durch die indirekte Adressierung über Zeiger. Sollte aber hier noch kein Problem sein.

Das hier würde ich mir sparen:

int Schieber_5_ClockPin = 24;  // Pin 24 das 2. Bit vom Port A (PINA PA2)
int Schieber_5_DataPin  = 25;  // Pin 25 das 3. Bit vom Port A (PINA PA3)

pinMode(Schieber_5_ClockPin, INPUT);
pinMode(Schieber_5_DataPin,  INPUT);

Mach das entweder in setup() oder verwende das DDR Register. Den entsprechenden Pin im DDR Register auf 1 = Ausgang, 0 = Eingang.
Wobei das in setup() vollkommen reicht, da es sich nie ändert. Wieso das also bei jedem Funktionsaufruf neu machen?

oh mann, du machst mich noch fertig.

das muss ich mir in ruhe anschauen wenn ich wieder am pc bin, wäre schon cool wenn das mit einer Funktion gehen würde.

ps. ja ich weiss das wie ich es aktuell mache überhaupt nicht perfekt ist.
jedoch ist das mein aktueller wissen stand.

Das ist eigentlich ganz einfach. Die Pin Nummer kannst du als Byte übergeben. Nichts ungewöhnliches daran.

Den Port musst du als Zeiger übergeben und dieser muss volatile sein, weil das Register so in der avr libc definiert ist. Daher einfach volatile byte* port als Parameter. Wenn man direkt PORTB als byte übergeben würden, hätte man den Inhalt des Registers!

Bei Aufruf übergibt man dann mit & die Adresse: &PORTB
Genauso wie bei Zeigern allgemein.

Und wenn du den Port ansprichst musst du den Zeiger mit * dereferenzieren:
*port =
Damit du das hast worauf der Zeiger zeigt und nicht etwas auf die Zeiger-Variable selbst zuweist.

Und bei PINA geht das genauso.

du darfst nicht so schnell mit mir das machen, ... ich bin nicht vom Fach :frowning:

Kannst das Beispiel:

Allgemeiner Test Code für die LED an Pin 13 (PB5):

mal genauer erklären, .. ich komme da nicht ganz mit.
könnten wir da einen Test Code machen mit dem wir die LED 13 Blinken lassen über die Register ?

könnten wir da einen Test Code machen mit dem wir die LED 13 Blinken lassen über die Register ?

Das ist genau was der Code macht. Lediglich um den Ausgang zu setzen, habe ich pinMode() verwendet.

Was gibt es da groß zu erklären? Das sind doch nur drei Zeilen die was interessantes enthalten und die habe ich schon erklärt :s

void blink(volatile byte* port, byte pin)
{
  *port ^= _BV(pin);   //Pin mit XOR invertieren
}

Eine Funktion die einen Zeiger auf ein Port Register und eine Pin Nummer als Parameter hat.

^= ist ein Exlusiv-Oder:

Das invertiert wie der Kommentar sagt den Pin

_BV() steht für "bit value" und ist lediglich ein Makro für "1 << n"
Mann kann genauso *port ^= 1 << pin schreiben

Wichtig ist hier das * vor "port", da du den Inhalt der Adresse ansprechen willst und nicht die Adresse selbst.

Und beim Aufruf übergibt man wie gesagt mit & die Adresse (ein Zeiger ist nichts anderes als eine Adresse):

blink(&PORTB, PB5);

Du würdest es so ähnlich machen:

float schieber(volatile byte* port, byte clock, byte data)
{
    if ( *port & ( 1 << data ))
      ....
  
    while ( !(*port & (1 << clock)))  { }

   ....
}

Und so aufrufen:

float value = schieber(&PINA, PA2, PA3);

Sollte rein theoretisch funktionieren.

Wir bleiben bei dem Blinkenden Beispiel.

hier Blinkt meine LED an PIN 13

void setup()
  {
    pinMode(13, OUTPUT);
  }

void loop()
  {
    digitalWrite(13, HIGH);   
    delay(1000);              
    digitalWrite(13, LOW);    
    delay(1000);            
  }

bei deinem Code Blinkt die LED nicht

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  blink(&PORTB, PB5);
  delay(500);
}

void blink(volatile byte* port, byte pin)
{
  *port ^= _BV(pin);   //Pin mit XOR invertieren
}

du weist doch ich bin nicht der schnellste bei der Sache.

Moment. Du bist auf einem Mega, oder? PB5 ist für den UNO. Auf dem Mega ist Pin 13 -> PB7

Im zweifelsfall nachsehen:
http://greentechhobby.com/images/Arduino/ArduinoMega.pdf

ja ok das erklärt einiges,

jetzt habe ich eine Fungzion die ich untersuchen kann, mal schauen ob ich das jetzt in meinen Schieber Code dann auch übertragen bekomme.

Dann mache ich mal eine Neue Datei für den Schieber Test :slight_smile:

Bis jetzt mache ich glaube ich gute Fortschritte :slight_smile:

die Originalzeile habe ich immer Auskommentiert und darunter die Neue Zeile mit dem Variablem Port Bezeichnung geätzt.

das ist der Bisherige Code:

#include "math.h"
unsigned long temp_millis;

void setup()
  {
    Serial.begin(9600);          // Seriele Einstellungen 
  }
  
void loop() 
  {
    float Ergebnis_Schieber = NAN; 
//    while ( isnan(Ergebnis_Schieber) )  { Ergebnis_Schieber = Schieber(); }   // Schieber abfragen
    while ( isnan(Ergebnis_Schieber) )  { Ergebnis_Schieber = Schieber(&PINA, PA0, PA1); }   // Schieber abfragen

    
    Serial.println(Ergebnis_Schieber);                                        // Schieber Wert ausgeben
  }  // ENDE --> void loop



// float Schieber()
float Schieber(volatile byte* port, byte clock, byte data)
  {
    int Schieber_5_ClockPin = 22;  // Pin 22 das 0. Bit vom Port A (PINA PA0) 
    int Schieber_5_DataPin  = 23;  // Pin 23 das 1. Bit vom Port A (PINA PA1) 

    pinMode(Schieber_5_ClockPin, INPUT);
    pinMode(Schieber_5_DataPin,  INPUT);

//    while (digitalRead(Schieber_5_ClockPin) == LOW) {}    // wenn CLOCK auf LOW, dann warten bis wieder HIGH
    while ( !(*port & (1 << clock)) ) {}                    // wenn CLOCK auf LOW, dann warten bis wieder HIGH

    temp_millis = millis();                                 // aktuelle Zeitnahme
//    while (digitalRead(Schieber_5_ClockPin) == HIGH) {}   // wenn CLOCK auf HIGH, warten bis wieder LOW
    while ( *port & (1 << clock) ) {}                       // wenn CLOCK auf HIGH, warten bis wieder LOW

    if ((millis() - temp_millis) > 100)                     // wenn High-Pegel länger wie 100ms war, springe in 'decode' Funktion
      {

        long value = 0;
        byte SIGN_Bit = 0;                          // Vorzeichen Bit 21
      
          for (int i=0;i<24;i++)                    // 24 Durchläufe, 0 bis 23
            {           
//              if ( PINA & ( 1 << PA1 ))             // wenn Data (Port.A Bit.1) HIGH ist, 
              if ( *port & ( 1 << data ))             // wenn Data (Port.A Bit.1) HIGH ist, 
                {                                   // eine 1 in die Variable 'value' an Bitnummer i
                  value |= 1UL << i;                // Syntax Hinweis aus dem Forum, wegen default 16 bit int Arithmetik
                }
//                while ( !(PINA & (1 << PA0)))  { }  // solange warten bis Clock (Port.A Bit.0) wieder HIGH ist
                while ( !(*port & (1 << clock)))  { }  // solange warten bis Clock (Port.A Bit.0) wieder HIGH ist

//                while (   PINA & (1 << PA0 ))  { }  // solange warten bis Clock (Port.A Bit.0) wieder LOW ist
                while (   *port & (1 << clock ))  { }  // solange warten bis Clock (Port.A Bit.0) wieder LOW ist

            }  // ENDE --> for Schleife
    
           SIGN_Bit = (value >> 21) & B01;          // Vorzeichen Bit 21 separieren
           value = value & 0xFFFF;                  // nur Bit 0 bis Bit 15 behalten, den Rest davor nullen
           value = value >> 1;                      // und alles um ein Bit nach rechts schieben, Startsignal wird entfernt
           if (SIGN_Bit == 1)                       // wenn Vorzeichen-Bit gesetzt war, alles mit -1 multiplizieren
            { value = value * -1; }
         return (float)value / 100;                 // Kommastelle setzen und man erhält den endgültigen Meßwert  (Rückgabe wert)
        }
        return NAN;                                 // Fehlerwert ausgeben 
  }  // ENDE --> Schieber

ich möchte jetzt aber noch den teil:

    int Schieber_5_ClockPin = 22;  // Pin 22 das 0. Bit vom Port A (PINA PA0) 
    int Schieber_5_DataPin  = 23;  // Pin 23 das 1. Bit vom Port A (PINA PA1) 

    pinMode(Schieber_5_ClockPin, INPUT);
    pinMode(Schieber_5_DataPin,  INPUT);

über die Register Sätzen.

Das müsste dann das Register DDRx sein.

und ich muss dann also
*port & clock = 0;

machen?

doch das macht eine Fehlermeldung, ... die Frage ist wie kann ich das Bit Sätzen?
ich dachte ich rufe das Register mit den Registerbit auf und sage das das Gleich 0 für Eingang ist.

Verwende halt pinMode() hier. Das muss doch nicht so schnell gehen

Ansonsten nimm am einfachsten die vordefinierten Makros um Bits zu setzen und zu löschen:
http://arduino.cc/en/Reference/BitSet
http://arduino.cc/en/Reference/BitClear

bitClear(DDRA, PA2);
bitClear(DDRA, PA3);

Und mach das in setup()!

Muecke:
und ich muss dann also
*port & clock = 0;

Was is das für eine Anweisung?

Wenn dann sowas in der Art

void func(volatile uint8_t *port, uint8_t mask)
{
 *port |= mask;
 
}

Das ist um ein Bit zu setzten. Um es zu löschen muss man mit dem Inversen verunden. Gerade hier sollte man wirklich die vorhanden Makros verwenden, wenn man da genaue Syntax nicht kennt.

sschultewolter:
Was is das für eine Anweisung?

da ich nicht wusste wie man das macht war das eine für mich Logische Erklärung das ich so ein Bit im Register Sätzen kann :slight_smile:

Lag irgend wie daneben; :-* :-*

Serenifly:
Verwende halt pinMode() hier. Das muss doch nicht so schnell gehen

Du hast recht das muss nicht schnell gehen, dachte mir nur das wenn ich jetzt clock udn data
schon übergebe dann könnte ich hier auch den Typ des Pin`s definieren,
und pinMode deshalb nicht da ich ja die Nr. nicht übergebe.
aus dem Grund dachte ich das ich das auch über das Register machen könnte.

Serenifly:
Und mach das in setup()!

ist das Schlimm wenn ich die x mal hintereinander neu Sätze?
ich dachte mir das ich so keinen PIN vergessen kann ihn zu Sätzen, wenn
ich das in der Funktion habe.
ich muss jetzt noch das DDRA mit übergeben.

so ich habe das gerade mal mit zwei Schiebern getestet :slight_smile:

und ich bin begeistert, DANKE DANKE DANKE

das ist echt genial.

hier mein Code.
Werde denn jetzt noch etwas Mehr an Kommentaren geben das ich auch später noch wies was ich wo eingeben muss. :grin:

#include "math.h"
  unsigned long temp_millis;
  float Ergebnis_Schieber = NAN;                                  // Ergebnis der Schieblehre nullen 

void setup()
  {
    Serial.begin(9600);          // Seriele Einstellungen 

       Serial.print("Schieber 4");
       Serial.print("\t");
       Serial.println("Schieber 5");

  }
  
void loop() 
  {
// Schiber 4 Ausgeben     --> PIN 24 & 25
    Ergebnis_Schieber = NAN;                                        // Ergebnis der Schieblehre nullen 
    while ( isnan(Ergebnis_Schieber) )                              // Plausibilitätskontrolle 
      { Ergebnis_Schieber = Schieber(&DDRA, &PINA, PA2, PA3); }     // Schieber abfragen
    Serial.print(Ergebnis_Schieber);                                // Schieber Wert ausgeben

       Serial.print("\t");

// Schiber 5 Ausgeben     --> PIN 22 & 23 
    Ergebnis_Schieber = NAN;                                        // Ergebnis der Schieblehre nullen 
    while ( isnan(Ergebnis_Schieber) )                              // Plausibilitätskontrolle 
      { Ergebnis_Schieber = Schieber(&DDRA, &PINA, PA0, PA1); }     // Schieber abfragen
    Serial.println(Ergebnis_Schieber);                              // Schieber Wert ausgeben
  }  // ENDE --> void loop



float Schieber(volatile byte* DDRport, volatile byte* PINport, byte clock, byte data)
  {
    
    bitClear(*DDRport, clock);                            // PIN auf eingang sätzen 
    bitClear(*DDRport, data);                             // PIN auf eingang sätzen 
    
    while ( !(*PINport & (1 << clock)) ) {}               // wenn CLOCK auf LOW, dann warten bis wieder HIGH

    temp_millis = millis();                               // aktuelle Zeitnahme
    while ( *PINport & (1 << clock) ) {}                  // wenn CLOCK auf HIGH, warten bis wieder LOW

    if ((millis() - temp_millis) > 100)                   // wenn High-Pegel länger wie 100ms war, springe in 'decode' Funktion
      {

        long value = 0;
        byte SIGN_Bit = 0;                                // Vorzeichen Bit 21
      
          for (int i=0;i<24;i++)                          // 24 Durchläufe, 0 bis 23
            {           
              if ( *PINport & ( 1 << data ))              // wenn Data (Port.A Bit.1) HIGH ist, 
                {                                         // eine 1 in die Variable 'value' an Bitnummer i
                  value |= 1UL << i;                      // Syntax Hinweis aus dem Forum, wegen default 16 bit int Arithmetik
                }
                while ( !(*PINport & (1 << clock)))  { }  // solange warten bis Clock (Port.A Bit.0) wieder HIGH ist

                while (   *PINport & (1 << clock ))  { }  // solange warten bis Clock (Port.A Bit.0) wieder LOW ist

            }  // ENDE --> for Schleife
    
           SIGN_Bit = (value >> 21) & B01;                // Vorzeichen Bit 21 separieren
           value = value & 0xFFFF;                        // nur Bit 0 bis Bit 15 behalten, den Rest davor nullen
           value = value >> 1;                            // und alles um ein Bit nach rechts schieben, Startsignal wird entfernt
           if (SIGN_Bit == 1)                             // wenn Vorzeichen-Bit gesetzt war, alles mit -1 multiplizieren
            { value = value * -1; }
         return (float)value / 100;                       // Kommastelle setzen und man erhält den endgültigen Meßwert  (Rückgabe wert)
        } 
        return NAN;                                       // Fehlerwert ausgeben 
  }  // ENDE --> Schieber

Hallo,

oh, hier ging es ja schon rege weiter. Sehr schön. Jetzt weis ich gar nicht ob mein Vorschlag noch benötigt wird. Ich stell ihn dennoch zur Verfügung. Vielleicht für Anregungen. Einmal mit 5 Funktionsaufrufen für die 5 Meßschieber. Und einmal in einer for Schleife zusammengefasst und einen Notausstieg falls bei einem Meßschieber das Clocksignal fehlt, wegen Kabelbruch oder sonstigen. Kannste ja mal testen.

Edit:
im zweiten Code "c" Fehler korrigiert, Notaus Variable für zweite Verwendung wurde nicht genullt

Digitalmessschieber_1x24Bit_Forum.zip (4.1 KB)

Hey Doc,

ja es hat sich einiges getan, es ist wirklich Sau doof das die Benachrichtigungen immer noch nicht gehen vom Forum (Hoffe das es bald wider geht).

Natürlich werde ich deinen Code noch testen und vilecht kann man das dann verbinden.
denn einen Notausstieg habe ich nicht bei mir :slight_smile:

so ich habe meinen Code zusammen geschrieben und auch alle Kommentare eingefügt,
die Funktion habe ich in eine *.h Datei ausgelagert (warum auch immer die ohne eine *.c Datei auskommt).

Leider habe ich noch keinen Idee wie ich das NAN aus der Funktion raus bekomme,
am besten wäre ein GOGO Anfang (ducken und weg hier... ) da man so was jedoch nicht schreibt verwende ich das nicht, es gibt bestimmt noch meine andere Lösung wie man das machen kann, jedoch ist mir bisher noch keine bekannt dafür :-(.

So klappt es aber schon sehr gut :slight_smile: und ich bin sehr zufrieden, bzw. das ist schon viel mehr als ich jemals erhofft habe hin zu bekommen.

Danke an alle die mitgewirkt haben.
Ich werde das auch noch mal im eigentlichen Thema der "Schieblehre - Arduino Versuchsaufbau" Verlinken.

Schieblehre_2_24bit.zip (3.7 KB)

Hallo,

die .h wird, denke ich, deshalb funktionieren, weil das eine reine Funktion ist die ausgelagert wurde. Macht aus meiner Sicht keinen Sinn. Deine 5 Meßschieber sind doch alle angeschlossen? Warum definierst Du die Eingänge mit jeden Funktionsaufruf neu? Das gehört einmalig in setup rein. Ansonsten, wenn's funktioniert ...

das ausgelagerte verschafft mir mehr übersieht in meiner *.ino Datei so habe ich das Gefühl des Überblick wider.
Du weist doch alles muss seine Ordnung haben, ... :wink:

das ist richtig: definieren der Eingänge, wird im Setup vorgenommen,
aus "Angst" eine nicht zu definieren habe ich das in die Funktion mit rein gepackt.

ich möchte den !Notausteig! von dir noch mit einbauen, das finde ich genial.