Go Down

Topic: ifdef ?! (Read 2422 times) previous topic - next topic

TERWI

Problemstellung:
Ich möchte je nach "Hardware-Ausstattung div. Pins an div. Arduinos belegen oder auch nicht.
D.h., ich gebe in einer "Init-Rotuine" vor, welche Pins überhaupt belegt sind und was sie darstellen sollen.
Z.B. Digital-In oder -Out sowie Analaog-In oder -Out.

Ich speichere das in einer Art Pin-Liste ab, was nicht belegt ist (eine -1) oder halt der zu nutzende Pin-Typ mit Nummer.

In der Loop soll nun "schnellstmöglichst" geprüft werden, ob der Pin überhaupt definiert ist.
Falls nein, gleich weiter, falls ja: Gibts was an / auf diesem Pin zu tun (lesen / schreiben).

Geht so was mit ifdef .... endif, bzw. oldfashioned mit if (pin > 0) oder sonst irgendwie ?
.... ich hätte da so etliche Abfragen zu tätigen und möchte die betreff Rechenzyklen in der Lopp so kurz wie's geht halten ...

Ideen, Vorschläge ?
To young to die - never to old for rock'n roll

Serenifly

#1
Jun 30, 2013, 05:42 pm Last Edit: Jun 30, 2013, 06:15 pm by Serenifly Reason: 1
ifdef und Konsorten sind Compile-Schalter (preprocessor directives). Da werden die Bedingungen bei Compilieren überprüft und nicht benötigter Code erst gar nicht übersetzt. D.h. du kannst keine Auswahl zur Runtime machen.

TERWI

Aaarrrghh - jetz wo du es schreibst .... gipps ja in Dehlphi au (benutze ich nur nie)
Also: Old-Fashiond-Way per IF ... ?
Oder gibt s da im Arduino was schnelleres als "Pin ist nicht definiert" ?
To young to die - never to old for rock'n roll

erni-berni

Hallo,
ich habe mal sowas gemacht um den Einsatz von 2 verschiedenen Displays (4x20 oder 2x16) in einem Programm möglich zu machen.
Hier die entscheidenden Zeilen:
Code: [Select]
// Code was developed for I2C LCD displays, selection should be done with #define I2CDISPLAY value.
// Debug with serial interface can be activated with define DEBUG

//#define DEBUG   
int dummy;    // this a workaround to avoid error messages with #define and #ifdef, see http://code.google.com/p/arduino/issues/detail?id=206
#define I2CDISPLAY

#define MAXLINES 4    // defines the number of display lines
#define LCD_CHARACTERS 16

#ifdef I2CDISPLAY
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define LCD_ADRESS 0x27
LiquidCrystal_I2C lcd(LCD_ADRESS,LCD_CHARACTERS,MAXLINES);  // I2C LCD address is 0x27
#else
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  // RS, E, D4, D5, D6, D7 - the standard connection
#endif

Den gesamten Code findest du hier http://forum.arduino.cc/index.php?topic=85537.0

Ist es das, was du willst?

Serenifly

Im Prinzip geht das schon mit defines. Aber nicht so wie du es hier beschreibst. Du musst das Programm dann für jede Hardware neu kompilieren, und vorher die defines richtig setzen, z.B. indem du die nicht zutreffendes auskommentierst.

TERWI

Ein klares: JEIN ! Jeweils neu komplieren käme efinitiv nicht in Betracht !
Versuch der Erklärung:
Ich habe eine sehr einfache Stepper-Hardware, eine etwas bessere und eine luxuriöse.
Die erste kann nur (stur) Dir, Steps und Speed annehmen und weiterleiten.
Die zweite ist erweitert in der Lage, auch Enable, Sleep sowie MicroStepping mit 2-3 Bits Auflösung zu verarbeiten.
Bei der dritten, habe ich auch noch die Möglichkeiten, neben Motor-Strom & -Temperatur auch noch "Events" ala End- und Notschalter auszulesen.
Dafür braucht es nun einen unterschiedlich großen Satz an Pins zum steuern und lesen, welchen ich im "Master-Programm" weiß und je nach Hardware entsprechend setzen müsste.
... Respektive auch auf (Interrupt-) Input reagieren muss.
Der Sketch im Arduino muss/soll dabei immer der gleiche bleiben - nur eben unterschiedlich initialisiert werden !
Dabei sollte der Sketch so "schlau" sein und Anfragen auf bzw. wegen Pin's, die nicht initialisiert worden sind, eben gleich 'abhaken'.
Sprich: Kenne ich nicht, weiter - as soon as possible.
To young to die - never to old for rock'n roll

Serenifly

#6
Jun 30, 2013, 07:08 pm Last Edit: Jun 30, 2013, 07:48 pm by Serenifly Reason: 1
Du bräuchtest doch generell eine Möglichkeit um zu erkennen welche Hardware dranhängt, oder? Ganz primitiv könnte man das z.B. mit einem Kippschalter einstellen. Oder wenn du die Motorsteuerung sagen wir mal über einen Sub-D Stecker o.ä. anschließt, könnte man einen nicht verwendenden Pin einmal auf LOW und einmal auf HIGH setzten. Das könnte man dann abfragen und jeweils anderen Code in der setup-Routine ausführen um die Pins zu initialisieren.

Wegen der Abfrage:
Wenn du ganze Funktionen auf einer bestimmten Hardware nicht ausführen willst, lagerst du das am besten in eine eigene Methode aus. Das hat den Vorteil, das es viel einfacher zu testen ist.

Du könntest auch für beide Versionen verschiedene Methoden schreiben (jedenfalls für die Stellen die unterschiedlich sind) und diese einmal per Function-Pointer zuweisen. Dann brauchst du nicht ständig abfragen was ausgeführt werden soll. Siehe hier:
http://www.learncpp.com/cpp-tutorial/78-function-pointers/

Würde dann so ähnlich aussehen:
Code: [Select]

void (*funcptr) (void);

void setup()
{
  if( //Hardware 1 //)
     functptr = function1;
  else  if( //Hardware 2 //)
     functptr = function2;
}

void loop()
{
   functptr();
}

void function1()
{ ... }

void function2()
{ ... }

TERWI

Natürlich weiß ich/man/Frau, welche Hardware dranhängt.
Sprich welches Steuershield (käuflich oder selbstgebastelt) und welcher Motor jeweils dranhängt.

.... nur mal so angenommen: Ich hab da was geniales gebastelt und stelle das zum freien Raum zum Gebrauch und
.... nicht jeder kann sich seine neue / eigene Version selbst kompilieren oder
.... man will mal selbst div Hardware / Motoren testen - so mal eben schnell on the fly und so ....

Mein Steuerprogramm (in Delphi !) berücksichtigt schon nahezu alles, was man sich so vorstellen kann.
Sprich jede mögliche Art von Betriebszuständen und Hardware.
Ich hätte eben gerne auch nur eine ( ! ) exakt dazu passende Arduino-Version und nicht Rev. A-Z Build 1-1000 für so mögliches allerlei.

Beispiel:
Wenn ich eine Hardware aus einem allgemeinem Steuerprogramm mit einem Sleep-Signal befeuere oder die Motor-Temp aus lesen will und diese Pins sind beim Setuo (eben auf diese spez. Hardware !) nicht belegt, dann soll da auch eben nix passieren.
Sprich: Pin ist assoziiert mit -1, also nicht definiert. Dann soll in der entsprechenden Abfrage-Routine halt eben MÖGLICHST WENIG an Prozessortakten verdöllmert werden - eben so, als wenn die Routine selbst gar nicht da wäre ....
To young to die - never to old for rock'n roll

Serenifly

#8
Jun 30, 2013, 08:04 pm Last Edit: Jun 30, 2013, 11:20 pm by Serenifly Reason: 1
Egal wie man das macht muss hat man wahrscheinlich irgendwo in der Loop Abfragen machen. Es geht wahrscheinlich dann eher darum diese zu minimieren statt sie zu elimieren.

Mit den Function-Pointern (haben oben mal ein Beispiel rein-editiert) könntest du mehrere komplett unterschiedliche Routinen haben, musst nicht jedesmal nachfragen was ausgeführt werden soll, und kannst diese beliebig umschalten. Aber wenn du nicht gemeinsamen Code in eigene Methoden auslagerst hast du duplizierten Code, wodurch wiederum Änderungen schwieriger werden. Und Methoden-Aufrufe kosten mehr Zeit als eine if-Abfrage. Das ist irgendwie ein Abwägen zwischen Lesbarkeit, Wartbarkeit und Performance.

volvodani

Ich denke du musst mindestens zwei Pin2 opfern für die erkennung der angelschlossenen Hardware.
Oder über einen Analog Eingang und den entsprechenden Netzwerk.
0V nix
0,8-1 Ver1
1,8-2V Ver2
2,8-3V Ver3
Damit wären ja mindestens 3 Boards erkennbar
oder digital
00= keine Hardware
01=Ver.1
10=Ver.2
11=Ver.3

Das kann man in der z.B.Setup Routine machen (wäre oldschool)

In der Loop
kannst du dann ja entscheiden z.B. am anfang die Funktionen reinbringen die alle brauche und dann die halt die Funktioen die andere brauchen

Code: [Select]


void loop()
funktionfueralle(),
if (Ver==1){
funktionenfuerVer1();}
if (Ver==2){
funktionenfuerVer2();}
if (Ver==3){
funktionenfuerVer3();}


Und wenn du dann Versionen gesetzt hast sind werden die Routinen auch nicht ausgeführt (kostest halt dich die Zeit der Prüfung ob diese Routine genutzt wird.
Andere Idee du machst das über z.B. über I2C Portexpander. Dann kann man eine I2C Scanner routine in die setup-Funktion reinbringen und je nach "ermittelender adresse" ist das halt das entprechende Board.
Gruß
Der Dani
0x2B | ~ 0x2B = 0xFF   
(Shakespeare)

TERWI

So dramatisch sollte das mit der Hardwareunterscheidung eigentlich nicht werden - Es geht halt eben nur um User (der zu bastelnden LIB), welche unterschiedliche Treiber Hardware besitzen.
Z. B. den Pololu A4988 oder den ich hier habe mit TB6560. Oder halt eben auch was selbgestricktes.
Das Kompilat auf dem Arduino sollte für alle gleich bleiben, damit das nachher nicht mit einer Flut an Versionen nur Verwirrung stiftet.

Natürlich muss dann die steuernde Soft auf nem PC oder sonstwas das auch wissen - sprich: Es macht keinen Sinn z.B. MicroSteps zu setzen, wenn die Hardware das nicht unterstützt.
Der Arduino soll das halt nur zur Sicherheit noch mal checken, falss ein anderer Progger das beim Steuerprogramm vergisst....

Ich würd's dann erst mal so machen:
Code: [Select]

void setup()
{

}

void loop()
{
    CheckSerial();
    if Steps2Run < 1 then GetNextCommand;
    if NumCMD < 1 then GetNextSequence;
    ......
}

void CheckSerial()
{
    /* aus eingegangenem Kommando die CMD-Nr und Werte auslesen */
    switch (CMD) {
      case 0:          /* Init_Pins */
      {
         Pin_Dir = val1;
         Pin_Step = val2;
         Pin_Speed = val3;
         .....
         Pin_Sleep = val10;
         Pin_MS1 = val11;
         Pin_MS2 = val12;
         Pin_MS2 = val13;
      }
      case 1:          /* Set CMD/Sequence */
         .....
      case 2:          /* Set MicroStepping */
        if (Pin_MS1 > 0) && (Pin_MS1 > 1) && (Pin_MS3 > 0) then {
          digitalWrite(Pin_MS1, val1);
          digitalWrite(Pin_MS2, val2);
          digitalWrite(Pin_MS3, val3); }
      break;
      case 3:          /* SetSleep */
        if (Pin_Sleep > 0) then
          digitalWrite(Pin_Sleep, val1);
      break;
      case 4:          /* GetTemp */
        if (Pin_Temp > 0) then
          TempRequest();
      break;
      ......
  }
}

Void TempRequest()
{
    int Temp = analogRead(Pin_Temp);
    SerialSend(3, Temp);

}

Void SendSerial(int CMD, int val)
{
    /* den zu sendenden String mit Steuerzeichen zusammenbauen */
    Serialprint(SendStr);
}

Ausser das der Arduino nur auf Befehle wartet, steckt da ausser der Befehlsauswertung, das Abarbeiten von Fahrsequenzen und der Rampenberechnung nix viel intelligentes drin.
To young to die - never to old for rock'n roll

Serenifly

#11
Jul 01, 2013, 12:00 pm Last Edit: Jul 01, 2013, 12:10 pm by Serenifly Reason: 1
Wenn du das mit Function-Pointern machen würdest hättest du für jede Version eine eigene CheckSerial Methode. Die wird dann einmal (z.B. in Setup oder durch Empfang von Daten vom PC) einem Pointer zugewiesen. In der loop rufst du dann jedes mal den gleichen Pointer auf, aber es können unterschiedliche Funktionen/Methoden dahinter stehen.

Du müsstest dann jedes mal wenn unterschiedliche Aktionen ausgeführt werden einen Pointer verwenden. Dadurch hast du am Ende viel mehr Methoden, aber die einzelnen Methoden selbst werden übersichtlicher und die meisten Abfragen würden wegfallen.

TERWI

Das werde ich auf jeden Fall mal im Auge behalten !  :.

Vor 2 Stunden sind endlich der UNO und der MEGA eingetroffen - Yepp Yepp.
Also gleich mal ran an die Buletten ....
.... schon geht das Theater los: Ich wollte den Messenger zum zerpflücken der Ser-Inputs benutzen. Error, Error, Error.
Aber dank dem Forum und "such such" bin ich da schon weiter - Es musste heissen Arduino.h statt WProgramm.h
-> Der MEGA spricht mit mir ! Ist wie Papa werden.  :smiley-mr-green:

PS:
Dieser Hökerfritze ist ja ein echt niedlicher. USB-Strippe dabei. Jaja, 1x unbrauchbar A auf A beim UNO und einmal A auf B beim MEGA - aber nur 20cm lang ....
To young to die - never to old for rock'n roll

Go Up