Frage: Direkte Port Manipulation

Hallo Forum,

DigitalRead() und DigitalWrite() sind mir zu langsam und habe nach Recherche einige interessante Seiten zur "direkten Port Manipulation" gefunden!

Ich habe ein Arduino Mega 2560 und würde gerne Pin 52 = PC1 (INPUT) lesen und den digitalen Zustand dieses Pins auf Pin 36 = PC1 (OUTPUT) schreiben.

Mit welchen Befehlen geht das lesen des Pins 52 und das schreiben des Zustands auf Pin 36?
Kann mir da jemand helfen, wie der Programmcode aussieht? Vielen Dank!

Mfg

DANINO24:
Ich habe ein Arduino Mega 2560 und würde gerne Pin 52 = PC1 (INPUT) lesen und den digitalen Zustand dieses Pins auf Pin 36 = PC1 (OUTPUT) schreiben.

Mit welchen Befehlen geht das lesen des Pins 52 und das schreiben des Zustands auf Pin 36?
Kann mir da jemand helfen, wie der Programmcode aussieht? Vielen Dank!

Hallo DANINO24,
zuerst muss man mal wissen, dass Pin 52 des Arduino-Boards das Bit 1 (also das 2. Bit) auf Port B des Controllers ist und Pin 36 das Bit 1 auf Port C des Controllers.
Dieses Wissen bekommt man durch Ansehen des Schaltplans hier http://arduino.cc/en/uploads/Main/arduino-mega2560_R3-schematic.pdf oder einfacher durch Benutzung dieser Tabelle hier:http://arduino.cc/en/Hacking/PinMapping2560 (rechte Spalte in der Tabelle).

Kleine Anmerkung: Pin 52 ist PCINT1/PB1 und nicht PC1.

Alsdann ist alles weitere ganz perfekt hier in einzelnen Schritten beschrieben:

(ganze Ports oder einzelne Port-Bits als Ausgang oder Eingang konfigurieren; bei Eingängen ggfs. die Pull-Up-Widerstände einschalten; Ports komplett setzen, Port komplett einlesen; Einzelbits setzen und lesen).

Wenn du danach glaubhaft machst, dass du dann noch Fragen hast (und welche) wird dir hier sicher weitergeholfen (versprochen!) :wink:

Diese Anmerkung nur deshalb, weil es meiner Meinung nach keinen Sinn macht, dir 5 oder 10 C-Anweisungen hinzuschreiben, wo dann die Erklärung, was da passiert, einen wesentlich größeren Umfang hat und es anderswo sowieso schon steht.

Viel Erfolg!

Gruß
Wolfgang

Pin 52 solltest Du vieleicht nicht nehmen da dieses Teil der SPI ist und somit die SPI-Schnittstelle blockierst.
Grüße Uwe

DigitalRead() und DigitalWrite() sind mir zu langsam

Wie schnell brauchst du denn ?

DigitalWrite und DigitalRead machen, wenn mit konstanten Pin-Nummern und Werten beauftragt, schon per Makro ziemlich "direkte Port-Manipulation".
Schau dir den Quellcode mal an. Da lernst du auch viel über "direct-port-manipulation".

Mein Tip, viel schneller wirst du womöglich nicht, aber evtl. kann dein Problem anders gelöst werden.

Ich bekomme es nicht hin! Muss mindestens 1.5 MHz schnell sein.

Habe nun den Input auf Pin 29 = PA7 und Output auf 22 = PA0.

Mein Code:

void setup(){
DDRA &= ~(1<<PA7);
DDRA|=(1<<PA0);
}

void loop(){
while(1){
PINA=(PINA & (1<< PA7));
}
}

Was ist falsch?]

PINA=(PINA & (1<< PA7)); // löscht alles bis auf Bit 7

ist sicher nicht was du willst.
Deiner Beschreibung nach willst du ein Stück Draht *) simulieren

PORTA = PORTA << 7; // wenn alle anderen Bits gelöscht werden können. Bit 7 enthält danach den Zustand von Bit 0.

oder etwas langsamer:

PORTA = PORTA & ~(1<<PA7) ) | PORTA << 7; // wenn die anderen Bits erhalten bleiben müssen

Wenn es nicht von Bit 0 nach 7 ginge, wäre noch etwas mehr Bitmaskiererei erforderlich.

Das hat jetzt nichts mit einem 1.5MHz Takt zu tun, oder willst du eigentlich was in der Richtung ?


*) Je nach den Umständen könntest du diesen Code auch durch ein Stück Draht, eine Diode oder irgend einen Logik-IC ersetzen

ich will folgendes programm mit direkter port manupilation umsetzen:

int testeingang=29;
int testausgang = 22;

void setup(){
pinmode(testeingang,input);
pinmode(testausgang,output);
}

void loop(){
digitalwrite(testausgang,digitalread(testeingang);

Wäre für jede hilfe sehr dankbar :-]

Ich würds bei Pin 52 und 36 lassen, da sparst Du die Bitschieberei.

// Pin 52 = Port B Bit 1
// Pin 36 = Port C Bit 1

void setup()
{
  // Setzt Pin 52 als INPUT
  DDRB = DDRB & B11111101;

  // Setzt Pin 36 als OUTPUT
  DDRC = DDRC | B00000010;
}

void loop()
{
  // Lese Pin 52 und Schreibe den Wert nach Pin 36
  PORTC = (PORTC & B11111101) | (PINB & B00000010);
}

Wie schnell ist denn nun

void setup(){

pinMode(13,OUTPUT); // for speed indication

DDRA &= ~(1<<PA7);
DDRA|=(1<<PA0);
}

void loop() {
  unsigned long timsignal;
  static boolean blinkstate; 
  PORTA = PORTA << 7;
  if (timsignal++ >= 1500000UL) {
      timsignal = 0;
      blinkstate = !binkstate;
      digitalWrite(13,blinkstate);
  }
}

?

Wenn eine LED an Pin 13 schneller als mit 0.5 Hz (1 sec an, 1 sec aus) blinkt, wäre deine Geschwindigkeitsanforderung 1.5 MHz erfüllt ?

Das hört sich perfekt an, danke! Werde es morgen mal testen....

MaFu:
Ich würds bei Pin 52 und 36 lassen, da sparst Du die Bitschieberei.

// Pin 52 = Port B Bit 1

// Pin 36 = Port C Bit 1

void setup()
{
 // Setzt Pin 52 als INPUT
 DDRB = DDRB & B11111101;

// Setzt Pin 36 als OUTPUT
 DDRC = DDRC | B00000010;
}

void loop()
{
 // Lese Pin 52 und Schreibe den Wert nach Pin 36
 PORTC = (PORTC & B11111101) | (PINB & B00000010);
}

Jetzt zum Hintergrund:

Ich habe als Input ein Taktsignal, das ca. 1,5 MHz schnell ist. Jetzt wollte ich schauen ob mein Arduino Board schnell genug ist dieses Signal zu verarbeiten. Sprich Taktsignal lesen und auf einen anderen Pin schreiben. Den Pin dann mit einem Oszilloskop betrachten und schauen, ob ich da auch 1,5 MHz erhalten.

Ich vermute mal das es knapp wird. Allerdings nicht wegen der Bitschieberei, die sollte schnell genug sein. Allerdings laufen außerhalb von loop() im eigentlich main() noch einige Serial-Event Abfragen die evtl. vom Compiler nicht wegoptimiert werden, jenachdem was sonst so im Sketch steht und damit wird dann Zeit zwischen den loop() Durchläufen verbraten.
Siehe main.cpp :

#include <Arduino.h>

int main(void)
{
        init();

#if defined(USBCON)
        USBDevice.attach();
#endif

        setup();

        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
        }

        return 0;
}

Habs grad mal zwei Versionen getestet.

unsigned long i;
unsigned long zeit;

void setup()
{
  DDRB = DDRB & B11111101;
  DDRC = DDRC | B00000010;

  Serial.print("\nTeste 1000000x direct port mapping\n");
  i = 0;
  zeit = micros();
}

void loop()
{
  if (i >= 1000000)
  {
    zeit = micros() - zeit;
    Serial.print("Dauer: ");
    Serial.print(zeit);
    Serial.print(" Mikrosekunden = ");
    Serial.print(zeit / 1000);
    Serial.print(" Millisekunden\n");
    while (1);
  }
  // Lese Pin 15 und Schreibe den Wert nach Pin 3
  PORTC = (PORTC & B11111101) | (PINB & B00000010);
  i++;
}

Wenn loop() immer wieder neu durchlaufen wird dauert es für 1 Million Lese/Schreib-Zugriffe 4904704 Mikrosekunden, also fast 5 Sekunden.

unsigned long i;
unsigned long zeit;

void setup()
{
  DDRB = DDRB & B11111101;
  DDRC = DDRC | B00000010;
}

void loop()
{
  Serial.print("\nTeste 1000000x direct port mapping\n");
  i = 0;
  zeit = micros();
  for (;;)
  {
    if (i >= 1000000)
    {
      zeit = micros() - zeit;
      Serial.print("Dauer: ");
      Serial.print(zeit);
      Serial.print(" Mikrosekunden = ");
      Serial.print(zeit / 1000);
      Serial.print(" Millisekunden\n");
      while (1);
    }
    // Lese Pin 15 und Schreibe den Wert nach Pin 3
    PORTC = (PORTC & B11111101) | (PINB & B00000010);
    i++;
  }
}

Fast der gleiche Code, allerdings wird loop() nicht mehr verlassen, sondern eine eigene Endlosschleife verwendet.
Dauer für 1 Million Lese/Schreib-Zugriffe ist nun 1383620 Mikrosekunden, also knapp 1,4 Sekunden.

Danke für die tolle Hilfe. Leider ist mein Mega 2560 definitiv zu langsam!

Jetzt überlege ich das Arduino Due mit 84 MHz zu kaufen.
Gibt es hier auch Quellen für die "direkte Port Manipulation" bzw. kann ich das obere Programm leicht umschreiben, um es auf dem Due auszuführen?

MfG

Ich befürchte, da sieht es mit Informationen noch mau aus. Zumindest hier im Deutschen Teil hab ich noch nirgends gelesen, dass einer schon den Due hätte.
Rein theoretisch müsste es beim Due auch so gehen und der sollte nach den Spezifikationen mehr als schnell genug dafür sein.

Darf man fragen, was eigentlich das Ziel des Projektes ist? Weil nur das Signal durchschleifen macht ja nicht gerade viel Sinn. :smiley:

DANINO24:
Danke für die tolle Hilfe. Leider ist mein Mega 2560 definitiv zu langsam!

Für mich erschließt sich noch nicht der Sinn der ganzen Aktion mit dem Lesen und Schreiben der Port-Bits. Geht es nur um das Prinzip, ob man ein 1,5 MHz-Eingangssignal mit einem mit 16 MHz betriebenen 8-Bit Mikrocontroller softwaremäßig verarbeiten kann oder steckt da noch etwas "sinnvolles" dahinter, was du uns noch nicht verraten hast (wobei natürlich auch der Lerneffekt und das Wissen sinnvoll sein kann) :wink:

Ich denke, auch wenn du einen schnelleren Controller verwendest und das Lesen und Schreiben der Port-Bits damit halbwegs funktionieren sollte, wirst du spätestens bei einer etwas umfangreicheren Verarbeitung des Eingangssignals an die Grenzen eines Mikrocontollers stoßen.

Das sollte dann meiner Meinung nach der Zeitpunkt sein, über das Konzept generell nachzudenken.

Ich sehe gerade, nachdem ich diesen Beitrag gepostet hatte, dass sich MaFu im Beitrag vorher die gleiche Frage gestellt hat :wink:

Wolfgang