Go Down

Topic: [Projekt] Schnelle Digital Eingabe/Ausgabe (Read 3904 times) previous topic - next topic

combie

Jan 14, 2016, 01:22 pm Last Edit: Nov 27, 2016, 06:32 am by combie
Hi!

Nachdem ich letztens in Timing Probleme getaumelt bin, habe ich mal versucht schnelle Ein/Ausgaben zu basteln. digitalRead() und digitalWrite() waren deutlich zu langsam.


Getestet auf einem Standard 16MHz UNO.

Versuch 1 zeigte: 54 KHZ Rechteck maximal
Code: [Select]

void loop()
{
  for(;;)
  {
    digitalWrite(13,!digitalRead(13));
  }
}


Versuch 2 zeigte: 100 KHZ Rechteck maximal
Code: [Select]

void loop()
{
  for(;;)
  {
    digitalWrite(13,1);
    digitalWrite(13,0);
  }
}

Das ist nicht die Welt!


Mit meinen neuen Pin Funktionen, 2 MHz Rechteck
Code: [Select]
void loop()
{
  for(;;)
  {
    togglePin(LED);
  }
}

Eine Steigerung um Faktor 20.


Die Pinbezeichnung orientiert sich hier nicht an der Arduino Pin Benennung, sondern an den AVR Datenblattangaben. Die jeweiligen Funktionen sind inline deklariert, und daran hält sich der Compiler. Auch wenn die Funktionen, mit ihren Parametern, fett aussehen, wird doch nur ein einziges Assembler Statement in den Code kompiliert. (Ausnahme: setInputPullup() wird zu 2 Statements)



Beispiel, Blink.ino:
Code: [Select]


#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);
}




Zum Abschluss natürlich noch die Pin.h, mit ihrer ganzen "Magie":
Code: [Select]
#pragma once


#ifndef __AVR__
 #error This FastPin Lib workls only with Atmel AVR
#endif

#ifndef ARDUINO
  #include <avr/io.h>
#endif  

#define PARAMETERLIST                            \
volatile __attribute__((unused)) uint8_t * DDR , \
volatile __attribute__((unused)) uint8_t * PORT, \
volatile __attribute__((unused)) uint8_t * PIN , \
                                 uint8_t   pin

/*
  * Benutzung: Pin für die spätere Verwendung deklarieren
  * #define BuildInLED PINDEF(B,5) // die LED auf einem Arduino UNO
*/
#define PINDEF(PORTNR,PINNR)  &DDR##PORTNR,&PORT##PORTNR,&PIN##PORTNR,PINNR



static inline void setPin(PARAMETERLIST)
{
 *PORT |= (1<<pin);  
}

static inline bool getPin(PARAMETERLIST)
{
 return (bool) *PIN & (1<<pin);  
}

static inline void unsetPin(PARAMETERLIST)
{
  *PORT &= ~(1<<pin);  
}

static inline void togglePin(PARAMETERLIST)
{
  *PIN = (1<<pin);  
}

static inline void setOutput(PARAMETERLIST)
{
  *DDR |= (1<<pin);  
}

static inline void setInput(PARAMETERLIST)
{
  *DDR  &= ~(1<<pin);  
}

static inline void setInputPullup(PARAMETERLIST)
{
  *DDR  &= ~(1<<pin);
  *PORT |=  (1<<pin);  
}





*viel Spaß damit*

Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

agmue

#1
Jan 14, 2016, 03:26 pm Last Edit: Jan 14, 2016, 04:59 pm by agmue
Ich stelle mich jetzt mal auf die Schulter von Giganten, wie es im Englischen so schön heißt, und ergänze millis() und Mega2560:
Code: [Select]
#include "Pin.h"

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

unsigned long aktMillis, altMillis;
int zeit = 1000;

void setup()
{
  setOutput(LED);
}

void loop()
{
  aktMillis = millis();
  if (aktMillis - altMillis >= zeit) {
    togglePin(LED);
    altMillis = aktMillis;
  }
}

Eine Sekunde an, eine aus, schneller Blinken tut das jetzt aber auch nicht   :smiley-grin:

Ich freue mich auf PinPlus, wo dann auch togglePinPlus(13); funktioniert.

Bis dahin gehört Pin.h zu meinen Standardbibliotheken.
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

combie

#2
Jan 14, 2016, 03:45 pm Last Edit: Jan 14, 2016, 04:05 pm by combie
Danke, für den Test auf dem Mega2560!

Quote
Eine Sekunde an, eine aus, schneller Blinken tut das jetzt aber auch nicht   :smiley-grin:
Ja! (wäre ja auch schlimm)
Aber es bleibt mehr Zeit in der loop() für anderes Gedöns.
Für die ganzen endlichen Automaten. ;-)



Quote
Ich freue mich auf PinPlus, wo dann auch togglePinPlus(13); funktioniert.
Ich habe mir vorher etliche Libs angesehen, welche das gleiche leisten wollen.
Die Umfomung von Arduino Pin zu PORT+Bit ist nicht so ganz trivial.
Nein: Das ist gruselig! (Dutzende von Extrawürsten wollen gebacken werden)

Da kann man besser irgendwas anderes nehmen:
Z.B.: https://github.com/watterott/Arduino-Libs/blob/master/digitalWriteFast/digitalWriteFast.h^


Quote
Bis dahin gehört Pin.h zu meinen Standardbibliotheken.
Schön, dass es dir gefällt!
Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

agmue

Es geht, nicht überraschend, auch mit Nano und ProMini, habe ich ergänzt.
Schön, dass es dir gefällt!
Tut es, weil es den Anfänger an die Ports heranführt, sollte denn "Blinken" im 2 MHz Takt gebraucht werden.
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

combie

#4
Jan 14, 2016, 09:15 pm Last Edit: Jan 14, 2016, 09:16 pm by combie
Quote
Es geht, nicht überraschend, auch mit Nano und ProMini,..
Ja, das habe ich auch wohl so erwartet...

Der Micro wäre noch interessant.
Da weiß ich auch noch nicht ob der togglePin() funktioniert.
Naja, das Datenblatt wirds mir wohl sagen...

Quote
..... habe ich ergänzt.
Schön..
Ich habe oben im Quelltext auch noch was ergänzt.

Ein netter Mensch hat mich darauf hingewiesen, dass der Code einen Eimer voll Warnings wirft, wenn man denn die Anzeige einschaltet. Von ungenutzten Funktionsparametern/Variablen spricht er. Da hat er natürlich recht, der Kollege, und auch der Compiler. Ein großer Teil der Funktionsparameter ist wirklich ungenutzt. Lässt sich bei der/meiner Strategie auch leider nicht vermeiden. Jetzt sind diese unbenutzten Variablen auch als solches gekennzeichnet. So bleibt der Compiler ruhig.

Feiner GNU Compiler!
 8)  Tut genau, was man von ihm will, man muss es ihm nur klar machen.  8)








Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

DrDiettrich

Bitte noch den Schreibfehler korrigieren:
Code: [Select]
static inline bool getPin(volatile UNUSEDVAR uint8_t * DDR, volatile UNUSEDVAR uint8_t * PORT, volatile uint8_t * PIN,uint8_t pin)
{
 return (bool) *PIN & (1<<pin);   // nicht |
}

Wenn das Ergebnis 0/1 sein soll, dann eher
 return (bool) (*PIN >> pin) & 1;

combie

#6
Jan 15, 2016, 10:27 am Last Edit: Jan 15, 2016, 11:13 am by combie
Quote
*PIN & (1<<pin);   // nicht |
Danke!
*geändert*
(Mist.., der ist mir durch die Lappen gegangen....)


Quote
Wenn das Ergebnis 0/1 sein soll, dann eher
 return (bool) (*PIN >> pin) & 1;
Der cast macht das schon.
Den (bool) Cast könnte man auch weglassen, wird sowieso durchgeführt.
Habe ich nur hingeschrieben um klar zu machen was passiert. Kostet keinen Code.

Auch müsste (*PIN >> pin) hier zur Laufzeit geschoben werden. Und schieben ist auf einem AVR teuer.
Dieses (1<<pin) schiebt der Compiler zur Compilezeit.

Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

Helmuth

#7
Jan 15, 2016, 12:02 pm Last Edit: Jan 15, 2016, 12:15 pm by Helmuth
Kannst ja mal hiermit gegentesten.

Und mal hier #6 lesen. Das Toggeln müsste in einem einzigen Takt funktionieren = 8 MHz Rechteck??

Gruß, H.

combie

#8
Jan 15, 2016, 12:24 pm Last Edit: Nov 25, 2018, 10:26 am by combie
Kannst ja mal hiermit gegentesten.
Aber gerne doch!


Code: [Select]

#include <fastpin.h>


Pin LED(13);

void setup()
{
  LED.setOutput();
}

void loop()
{
  for(;;)
  {
    LED.toggle();
  } 
}


Mehr, als ein 800KHz Rechteck ist nicht drin.
Und der Speicherverbrauch ist erheblich größer.
Sowohl Flash, als auch Ram.
OK, Ram ist kein Wunder, denn meins braucht keins.

Und mal hier #6 lesen. Das Toggeln müsste in einem einzigen Takt funktionieren = 8 MHz Rechteck??
Das wäre schön.. (Takte korrigiert)
1 Takt fürs Toggeln
2 Takte für den Sprung in der For Schleife
Macht 3 Takte für eine Halbwelle
6 Takte für eine Vollwelle

16MHz / 6 Takte = 2,66667MHz
Mehr/Schneller geht einfach nicht.
Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert


RudiDL5

Nur so als Spaß zwischen Frühstück und duschen:

Code: [Select]
void setup()
{
  pinMode( 13, OUTPUT );
}
void loop()
{
  // Auf UNO R3 mit 16 MHz ergibt diese
  // Schleife ein Rechteck von 2,65 MHz
  asm volatile(
  "0:      sbi   %[pob],    5     \n\t"
  "        cbi   %[pob],    5     \n\t"
  "        rjmp  0b               \n\t"
  :
  : [pob]  "I"   (_SFR_IO_ADDR(PORTB))
  :
  ); 
}

;-)

combie

#11
Jan 15, 2016, 12:55 pm Last Edit: Jan 15, 2016, 01:06 pm by combie
Danke.
Gerne!

Habe auch schon Experimente gemacht, das irgendwie mit OOP und Templates so schlank zu bekommen. Ist mir bisher noch nicht gelungen. Und von den ganzen OOP getriebenen Pin Manipulatoren, die mir bisher unter gekommen sind, ist das Fastled Gedöns noch eins der besten.

Quote
Nur so als Spaß zwischen Frühstück und duschen:
Das ist dann aber kein symmetrischer Rechteck mehr!
Exakt gleiches Ergebnis, wie mein Code:
Code: [Select]

#include "Pin.h"

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

void setup()
{
  setOutput(LED);
}

void loop()
{
  for(;;)
   {
    togglePin(LED);
    togglePin(LED);
   }

}

Auch 2,667MHz und auch kein symmetrischer Rechteck mehr.

Assembler bringt hier keine Vorteile.
Aber als Gegenprobe, eine feine Sache!
Danke.


In dieser Variante symmetisch und 2MHz
Code: [Select]
void loop()
{
  // Auf UNO R3 mit 16 MHz ergibt diese
  // Schleife ein Rechteck von 2 MHz
  asm volatile(
  "0:      sbi   %[pob],    5     \n\t"
  "        rjmp  0b               \n\t"
  :
  : [pob]  "I"   (_SFR_IO_ADDR(PINB))
  :
  ); 
}
Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

Doc_Arduino

Hallo,

was störte dich an digitalWriteFast? Nicht perfekt genug? Immer noch zu langsam. Kommt man da nicht besser ohne extra Lib direkt den Pin zu schalten. Im klassischen C direkt das Bit im Port zu schalten.

Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

combie

#13
Jan 15, 2016, 05:54 pm Last Edit: Jan 15, 2016, 05:54 pm by combie
So:
Code: [Select]
PORTB |= (1 << PB5);
? ? ?

Da ist mir doch:
Code: [Select]
setPin(LED);
lieber.
Ist besser lesbar, finde ich.

Der Compiler macht sowieso aus beidem das selbe.

---------

Quote
was störte dich an digitalWriteFast? Nicht perfekt genug?
;-) vielleicht ist es das ;-)

Code: [Select]
void loop()
{
  for(;;) // auch 2MHz
  {
    fastDigitalToggle(13);
  }
}
Wer seine Meinung nie zurückzieht, liebt sich selbst mehr als die Wahrheit.

Quelle: Joseph Joubert

volvodani

Danke für die arbeit. Wenn man schnellen  PinChange braucht perfekt.
Damit könnte man dann perfekt ein Software PWM machen nur in der Loop.
Karma +

Gruß
DerDani
"Komm wir essen Opa!" - Satzzeichen retten Leben!

Go Up