Probleme mit digitalRead() bzw. digitalWrite()

Moin, moin…

Ich bin neu im Thema Arduino und möchte mir eine Sicherheitsschaltung für meinen RC Truck bauen.

Die Grundidee : Die Arduino Platine empfängt auf einem Pin das Servosignal für den Fahrregler (Vorwärts/Rückwärts) und leitet diesen auf einem anderen Pin weiter. In regelmäßigen Abständen misst die Platine mit einem Ultraschallsensor (Parallax) die Entfernung zu nahen Objekten. Wenn ein Objekt näher als 10 cm ist, soll die Weiterleitung des Signals unterbrochen werden, der Truck hält an.

Dazu habe ich eine entsprechende Platine und ein Breadboard bestückt. Eine grüne LED sagt alles OK, eine rote sagt Hindernis!!.

Soweit so gut.

Hier nun erst einmal der Code:

int    digitalIn  = 8;
int  digitalOut  = 11;
const int ledPinRed = 5;
const int ledPinGreen = 3;
const int sensorPin = 4;

int  nDistance  = 999;
int  nLastMillis  = 0;

void setup( )
{
  Serial.begin( 9600 );
  pinMode( digitalIn, INPUT );
  pinMode( digitalOut, OUTPUT );
  pinMode( ledPinRed, OUTPUT );
  pinMode( ledPinGreen, OUTPUT );
  nLastMillis  = millis( );
 }

int GetDistance( )
{
    // wir intialisieren den Sensor, indem wir ihm einen kurzen "Low"-Pulse senden,
    // dann einen 5 mS "High-Pulse"
  
  
    pinMode(sensorPin, OUTPUT);
    digitalWrite(sensorPin, LOW);
    delayMicroseconds(2);
    digitalWrite(sensorPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(sensorPin, LOW);
  
    // wir benutzen denselben Pin als Eingang und berechnen die Dauer des Pulses zwischen
    // ausgehendem und eingehendem Signal
  
    pinMode(sensorPin, INPUT);
    nDistance = pulseIn(sensorPin, HIGH) / 29 / 2;

  // wir berechnen die Entfernung aus der Zeitdifferenz

}

void loop()
{
  int  nMillis  = millis( );
  
  if ( ( nMillis - nLastMillis ) > 500 )
  {
    //  Die letzte Messung war mehr als 500ms her
    GetDistance( );
      
     nLastMillis = nMillis;
  }
  
  
  if ( nDistance < 10 )
  {
     //  Hier machen wir erst einmal nix.
     //  Spaeter soll hier ein Null-Signal (Knueppel losgelassen) simuliert werden.
    digitalWrite( ledPinRed, HIGH );
    digitalWrite( ledPinGreen, LOW );
  }
  else
  {
    digitalWrite( ledPinRed, LOW );
    digitalWrite( ledPinGreen, HIGH );
   digitalWrite( digitalOut, digitalRead( digitalIn ) );
  }
    delay(10);
 
}

Mein Problem ist nun, dass die Zeile digitalWrite( digitalOut, digitalRead( digitalIn ) ); IMMER ausgeführt wird, egal ob die rote oder die grüne LED leuchtet, egal ob der if oder der else Zweig ausgeführt wird. Wenn ich die Zeile in den If Block packe, steht der Servo am Anfang erst einmal still, es erfolgt keine Weiterleitung. Erst wenn ein Hindernis auftaucht bewegt er sich. Nehme ich dann das Hindernis aber wieder weg, schaltet die grüne LED wieder, aber die Weiterleitung bleibt aktiv !!!

Ich verstehe es nicht. Es scheint so als ob Arduino nach dem ersten read/write die Verbindung dauerhaft herstellt.

Kann mir jemand sagen was hier schief läuft ?

Greetinx
Peter

Das funktiniert so nicht. Du machst einen Denkfehler.
Das RC Signal zum Fartenregler ist ein PPM signal das einen Impuls liefert der zwischen 1 und 2 mS lang ist.

Dein Kode ist durch Pulsein und Delasy so langsam daß das PPM Singal der Eingangspins nicht an das Ausgangspin weitergeleitet wird.

Du mußt die Dauer des PPM Signal einlesen und es gleich ausgeben oder bei zu nahen Gegenständen modifiziert.

Grüße Uwe

Moin Uwe,

das würde aber bedeuten dass ich das Signal erst messen müsste und danach ein gleicher Form abgeben müsste. dies würde eine - wenn auch kleine - Verzögerung darstellen.

Was ist letztendlich erreichen will ist dass wenn am In Pin ein Signal anliegt, der Out pin ebenfalls ein Signal erhalten soll.Wenn das Signal auf LOW geht, soll der Out PIN ebenfalls auf LOW gehen. Dadurch spiegele ich den Impuls auf den Out Pin.

Was mich sehr irritiert ist, dass die Arduino Platine bei einem Hindernis in die if Anweisung geht, also in dem Moment die read/write Anweisung nicht aufgerufen wird. Der Servo folgt aber nach wie vor meinen Knüppelbewegungen.

Wenn ich die read/write Anweisung in die if Anweisung verschiebe, sollte nach meiner Logik die Weiterleitung des Signals nur erfolgen wenn ein Hindernis da ist. Nach einem Reset ist auch erst einmal Stille, der Servo tut nix. Sobald ein Hindernis erkannt wird, folgt der Servo meinen Bewegungen. Nehme ich das Hindernis wieder weg sollte die Weiterleitung wieder aufhören. Es wird auch wieder der else Zweig durchwandert was man an den LEDs sehr gut sehen kann. Jedoch bleibt ab dem Auftauchen des ersten Hindernis die Weiterleitung bis zum nächsten Reset aktiv.

Greetinx Peter

gismow: Moin Uwe,

das würde aber bedeuten dass ich das Signal erst messen müsste und danach ein gleicher Form abgeben müsste. Dies würde eine - wenn auch kleine - Verzögerung darstellen.

Genau; nur ist die Verzögerung im MilliSekunden Bereich. Also irrilevant.

gismow: Was ist letztendlich erreichen will ist dass wenn am In Pin ein Signal anliegt, der Out pin ebenfalls ein Signal erhalten soll.Wenn das Signal auf LOW geht, soll der Out PIN ebenfalls auf LOW gehen. Dadurch spiegele ich den Impuls auf den Out Pin.

Dann kannst Du aber kein delay(10); in die Loop-Schleife reinschreiben. Damit das Ausgangssignal eine Genauigkeit von 256 Schritten hat muß die Abfrage des PPM Signals alle 4 µS!!!! erfolgen.

gismow: Was mich sehr irritiert ist, dass die Arduino Platine bei einem Hindernis in die if Anweisung geht, also in dem Moment die read/write Anweisung nicht aufgerufen wird. Der Servo folgt aber nach wie vor meinen Knüppelbewegungen.

Dazu kann ich nichts sagen.

gismow: Wenn ich die read/write Anweisung in die if Anweisung verschiebe, sollte nach meiner Logik die Weiterleitung des Signals nur erfolgen wenn ein Hindernis da ist. Nach einem Reset ist auch erst einmal Stille, der Servo tut nix. Sobald ein Hindernis erkannt wird, folgt der Servo meinen Bewegungen. Nehme ich das Hindernis wieder weg sollte die Weiterleitung wieder aufhören. Es wird auch wieder der else Zweig durchwandert was man an den LEDs sehr gut sehen kann. Jedoch bleibt ab dem Auftauchen des ersten Hindernis die Weiterleitung bis zum nächsten Reset aktiv.

Du meinst wenn kein Hinderniss da ist.

Welchen Abstandssensor hast Du? einen SRF05?

Grüße Uwe

Moin Uwe,

ich habe es auch ohne das Delay probiert, es ist das gleiche Ergebnis.

Beim Sensor handelt es sich um

PING)))™ Ultrasonic Distance Sensor (#28015) von Parallax.

Greetinx Peter

Wieso reichst Du das Signal im Arduino durch? Wäre es nicht sehr viel einfacher das Signal mit einem Transistor zu schalten? D.h. der Transistor leitet das Signal - je nachdem wie der Arduino ihn ansteuert - durch oder nicht durch.

Im allereinfachsten Fall reicht vieleicht sogar ein Widerstand. D.h. das Signal wird mit einem 200 - 1000 Ohm Widerstand an Fahrtregler geleitet. Gleichzeitig ist ein Arduino Pin am Regler angeschlossen. Wenn das Signal ankommen soll wird der Pin auf "input" und "low" also auf "hochohmig" gestellt. Wenn das Signal nicht ankommen soll wird er auf "output" und "low" also auf 0V gezogen. Dadurch wird das Signal nach Masse abgeleitet.

Moin Udo,

daran habe ich auch erst gedacht. Allerdings schaltet das das Signal ja komplett ab, was ich nicht möchte.

Das Servo Signal hat immer einen Impuls. Wenn man den Hebel zurück zieht, ist der Impuls 1ms lang, in der Mittelstellung 1,5ms und ganz vorne 2ms.

Somit brauche ich für einen Stop ein Impuls von 1,5ms.

Mein finales Ziel ist folgendes:

Ich prüfe bei einem Hindernis das Signal. Wenn der Impuls größer als 1,5ms ist ist das Auto im Vorwärtsgang. Dieser Impuls soll auf ein Impuls von 1.5ms korrigiert werden. Wenn der Impuls kleiner 1,5ms ist, liegt der Hebel im Rückwärtsgang. Dieser Impuls ist “legal”, denn dann möchte sich das Auto ja vom Hindernis wieder entfernen.

Somit brauche ich am In Pin grundsätzlich das signal um es letztendlich auswerten zu können.

Greetinx
Peter

Oder Du nimmst 2 Pins. Der Auswertepin bekommt direkt das Signal. Dann wird das Signal mit einer Diode und 1 Widerstaend so entkoppelt, dass der Auswertepin noch was sehen kann, der Einspeisepin aber alles überschreiben kann. Zur Not muß noch ein Treiber dazwischen.

Alternativ kannst Du auch die Zustandswechsel in einer kurzen Interruptroutine einfach durchkopieren. Dann hast Du natürlich keinen Hardwareaufwand. Ohne Interrupts wird das allerdings schwierig.

Moin Udo,

und da steige ich dann aus... Ich bin nur Anfänger. Könntest Du die Schaltung der Diode etwas genauer erläutern ? Das wäre cool :)

Wie funktioniert das denn mit den Interrupts ? Das Du einen interessanten Link zum Lesen (für Anfänger geeignet) ?

Vielen Dank :-)

Greetinx Peter

Moin Udo,

ich habe mal ein wenig mit Interrupts experimentiert und konnte das Problem einkreisen. Es scheint mit dem Zugriff auf Variablen zu tun zu haben.

Hier mein Code unter Verwendung der PinChangeInt library.

#include <PinChangeInt.h>

#define INPUTPIN  2

#define LEDREDPIN  4
#define LEDGREENPIN 5
#define LEDYELLOWPIN 6

#define SENSORPIN  7
#define OUTPUTPIN  13

volatile uint8_t *pinT_OPort;
volatile uint8_t pinT_Mask, not_pinT_Mask;
volatile uint8_t pintest, pinIntLow, pinIntHigh;

uint8_t  nGlobalDistance;

void quicfunc() 
{
  if ( nGlobalDistance < 10 )
  {
    digitalWrite( LEDYELLOWPIN, HIGH );
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
   }
  else
  {
    digitalWrite( LEDYELLOWPIN, LOW );
    if ( PCintPort::pinState == LOW )
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
    else
     *pinT_OPort|=pinT_Mask;        //  Output to HIGH
  }
}

void setup() {
  Serial.begin( 9600 );
  pinMode(INPUTPIN, INPUT); digitalWrite(INPUTPIN, LOW);
  PCintPort::attachInterrupt(INPUTPIN, &quicfunc, CHANGE);
  
   pinMode(OUTPUTPIN, OUTPUT); digitalWrite(OUTPUTPIN, LOW);
   pinMode(LEDREDPIN, OUTPUT); 
   pinMode(LEDGREENPIN, OUTPUT); 
   pinMode(LEDYELLOWPIN, OUTPUT);
  // *****************************************************************************
  // set up ports for output ************ 
  // *****************************************************************************
  pinT_OPort=portOutputRegister(digitalPinToPort(OUTPUTPIN)); // output port
  pinT_Mask=digitalPinToBitMask(OUTPUTPIN);                   // mask
  not_pinT_Mask=pinT_Mask^0xFF;                           // not-mask
}

uint8_t i;
void loop() {

    //  Hier pruefen wir nun den Sensor.
     long dauer, cm;

  // wir intialisieren den Sensor, indem wir ihm einen kurzen "Low"-Pulse senden,
  // dann einen 5 mS "High-Pulse"


  pinMode(SENSORPIN, OUTPUT);
  digitalWrite(SENSORPIN, LOW);
  delayMicroseconds(2);
  digitalWrite(SENSORPIN, HIGH);
  delayMicroseconds(5);
  digitalWrite(SENSORPIN, LOW);

  // wir benutzen denselben Pin als Eingang und berechnen die Dauer des Pulses zwischen
  // ausgehendem und eingehendem Signal

  pinMode(SENSORPIN, INPUT);
  dauer = pulseIn(SENSORPIN, HIGH);

  // wir berechnen die Entfernung aus der Zeitdifferenz

  int nDistance = dauer / 29 / 2;
  
  nGlobalDistance = (uint8_t)nDistance;

  Serial.print( nDistance );
  Serial.print ( " - " );
  Serial .println( ( nDistance < 10 )? "Warning" : "OK" );
  
  digitalWrite( LEDREDPIN, ( ( nDistance < 10 ) ? HIGH : LOW ) );
  digitalWrite( LEDGREENPIN, ( ( nDistance >= 10 ) ? HIGH : LOW ) );
  
  delay( 200 );
}

Ich habe nun festgestellt, dass ich das gleiche Problem habe wie vorher, allerdings konnte ich das Problem eingrenzen. Ich setze in nGlobalDistance die Entfernung, welche vom Sensor geliefert wird. Ich habe nun festgestellt, dass die if Anweisung in der callback Funktion bezüglich der Distanz solange funktioniert, bis die loop Funktion der Variable den ersten Wert zugewiesen hat.

Sobald also die Anweisung

nGlobalDistance = (uint8_t)nDistance;

erfolgt, ist die Variable für die callback Funktion scheinbar nicht mehr korrekt auswertbar. Scheinbar ist ein extrem hoher Wert aus Sicht der callback drin, denn ab diesem Zeitpunkt geht die callback IMMER in den else Zweig, egal was ich der nGlobalDistance Variable in der loop Funktion zuweist. Die loop funktion wiederum setzt die LEDs sauber entsprechend dem Schwellenwert von 10 cm.

Ich habe dann mal in der callback Funktion die Zeile

  Serial.println( nGlobalDistance );

eingefügt. Maximal 3 mal sehe ich eine Ausgabe nach den reset der Platine, danach nie wieder…

Ich bin etwas ratlos…

Für Tipps und Tricks wäre ich sehr dankbar.

Greetinx
Peter

Dein vorgehen scheint mir nicht besonders zielführend.

  1. Was ist denn eine callback Funktion?
  2. Interrupts und delay vertragen sich meines Wissens nicht.
  3. Ist deine Interrupt-Funktion ziemlich lang.
  4. Wie vertragen sich pulsein und Interrupts?

Wie wäre es mit folgendem Vorgehen:

  1. Distanz bestimmen.
  2. Wenn Abstand groß genug:
    Mit pulsein das eingehende Signal messen
    das Signal Über PWM ausgeben
    Grüne LED leuchten lassen
  3. Wenn Abstand zu klein:
    Über PWM das Stopp-Signal ausgeben
    rote LED anschalten
    Das das PWM-Signal über Hardware erzeugt wird hast du immer einen definierten Zustand ohnd, dass du dauern in einen Interrupt musst.

Moin circuit99,

in wie weit ist es nicht zielführend ?

Ich suche nach einer Möglichkeit ein Signal verlustfrei zu lesen und weiterzuleiten. Sollte ein bestimmter Grund vorliegen, in meinem Fall ein Hindernis, soll dieses Signal nicht weitergeleitet werden, sondern durch ein definiertes Signal (1,5ms HIGH + 18,5ms LOW - entspricht Nullstellung des Knüppels an der Funke - sprich Stop). Dies ist in meinen Code noch nicht realisiert, so weit bin ich noch nicht.

Aber nun zu Deinen Punkten:

Zu 1.: Eine callback Funktion ist in der EDV eine Funktion, welche man im System an einen Hard- oder Software Event bindet. Wenn der Event auslöst, wird diese durch den Programmierer definierte Funktion aufgerufen.

Zu 2: Warum sollen sich Interrupts und ein delay in Mainloop (ich vermute Du meinst das delay in der loop() Funktion) nicht vertragen ? Interrupts zeichnen sich in der Regel dadurch aus dass sie parallel zu anderen Prozessen arbeiten. Somit sollte es eigentlich vollkommen egal sein was im Hauptprozess passiert. So verhält es sich zumindest in den EDV Systemen mit denen ich vertraut bin. Ein grundlegendes Problem ist allerdings ein Locking der Variablen um ein gleichzeitgen Zugrif zur Änderung zu vermeiden.

Zu 3: Die digitalWrite Aufrufe habe ich nur für Debug Zwecke kurzzeitig eingesetzt um festzustellen wann die if bzw. else Anweisung durchlaufen wird. Somit besteht die eigentliche Interrupt Routine lediglich aus 2 ineinander geschachtelten if Anweisungen und max. einer logischen Operation. Das sollte eigentlich nicht zu lang sein.

Zu 4: Bis auf das Issue der nicht auswertbaren Variable sehr gut. Die Ansteuerung des Servos über den Interrupt funktioniert einwandfrei ohne auch nur ein kleinstes Ruckeln. Der Sensor arbeitet absolut zuverlässig und im Takt, ohne merkliche Verzögerungen.

Dein Vorschlag zur Vorgehensweise klingt logisch, hat aber einen Nachteil, welcher in der Art und Weise der Signalübertragung von Servos begründet liegt.

Ein analoger Servo hat in der Regel einen Schwenkbereich von 180°. Um einem analogen Servo den aktuellen Stellwinkel zu vermitteln verwendet der Sender Impulsbereiche von jeweils 20ms. Der Servo erhält also 50 Befehle pro Sekunde. Am Anfang des Impulses wird der Level auf HIGH gezogen. Die nun folgende Dauer des HIGH Levels bestimmt den Winkel des Servos. Ein Winkel von 0° (voller Ausschlag nach links) wird angezeigt durch eine Dauer von 1ms, die Mittelstellung (90°) durch 1,5ms und der rechte volle Ausschlag (180°) durch eine Dauer von 2ms. Danach wird das Signal auf LOW gezogen und bleibt bis zum Ende des 20ms Impulses auf LOW.

Das Auslesen des Ultraschallsensensors dauert seine Zeit, auch wenn es nur Micosekunden sind. Bei einem Abstand von ca. 10 cm dauert das Pulsen und Lesen des Ultraschallsignals ca. 500 Microsekunden. Dies stellt im Bereich des Servo HIGH Signal bereits eine Winkeldifferenz von 90° dar. Startet ein neuer Servoimpuls zeitlich genau mit einen Sensorausleseprozess, wird das HIGH Element des Impulses nicht komplett erkannt und hat eine zu kurz gemessene Dauer.Somit liefert das Arduino Borard bei der Weiterleitung einen falschen Winkel, dessen Abweichung definitiv nicht zu vernachlässigen ist. Ich habe auch dieses ausprobiert und das Ergebnis war ein heftiges Zucken mit Winkelabweichungen bis zu 90°. Eine serielle Abarbeitung à la messen, pulsen und anschließende Signalweiterleitung ist also leider keine Alternative.

Greetinx Peter

Ich verstehen nicht wieso Du direkte Portmanipulation und digitalWrite im Callback mischst. Aber das ist nicht das Problem. Serial... hat auf jeden Fall nichts in ISR Callbacks verloren - dazu ist Serial einfach viel zu langsam. Der Erfolg ist dann, dass Dein Prozessor "einzufrieren" scheint. Und natürlich hast Du völlig Recht, dass delay im loop() kein Problem ist.

Aber: was auf jeden Fall ein Problem ist (nicht notwendig die Ursache, aber es ist ein Problem):

Du hast nGlobalDistance nicht als volatile deklariert obwohl Du es in einer ISR verwendest. Das kann gut gehen oder schief gehen, je nach Mondphase, Compilereinstellungen oder Karma, oder auch einfach nur abhängig von der Luftfeuchtigkeit...

Moin Udo,

vielen Dank für die Tipps. Die Verwendung von digitalWrite und Serial Methoden sind auch nur mein verzweifelter Versuch zu erkennen was schief geht. In der Originalversion war das auch nicht drin.

Den Tipp mit volatile werde ich testen sobald ich wieder an der Platine bin. Ich gebe Bescheid was es ergeben hat.

Greetinx Peter

Moin Udo,

ich habe gerade mal den Code entrümpelt. Die Serial Klasse ist komplett raus und die gelbe LED ist auch weg. Die Variable nGlobalDistance ist nun eine Volatile Variable.

Leider hat es nichts gebracht, die if Anweisung greift immer noch nicht. Hier der aktuelle Code.

#include <PinChangeInt.h>

#define INPUTPIN  2

#define LEDREDPIN  4
#define LEDGREENPIN 5

#define SENSORPIN  7
#define OUTPUTPIN  13

volatile uint8_t *pinT_OPort;
volatile uint8_t pinT_Mask, not_pinT_Mask;
volatile uint8_t pintest, pinIntLow, pinIntHigh;

volatile uint8_t  nGlobalDistance;

void quicfunc() 
{
  if ( nGlobalDistance < 10 )
  {
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
   }
  else
  {
    if ( PCintPort::pinState == LOW )
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
    else
     *pinT_OPort|=pinT_Mask;        //  Output to HIGH
  }
}

void setup() {
//  Serial.begin( 9600 );
  pinMode(INPUTPIN, INPUT); digitalWrite(INPUTPIN, LOW);
  PCintPort::attachInterrupt(INPUTPIN, &quicfunc, CHANGE);
  
   pinMode(OUTPUTPIN, OUTPUT); digitalWrite(OUTPUTPIN, LOW);
   pinMode(LEDREDPIN, OUTPUT); 
   pinMode(LEDGREENPIN, OUTPUT); 
  // *****************************************************************************
  // set up ports for output ************ 
  // *****************************************************************************
  pinT_OPort=portOutputRegister(digitalPinToPort(OUTPUTPIN)); // output port
  pinT_Mask=digitalPinToBitMask(OUTPUTPIN);                   // mask
  not_pinT_Mask=pinT_Mask^0xFF;                           // not-mask
}

void loop() 
{
  // wir intialisieren den Sensor, indem wir ihm einen kurzen "Low"-Pulse senden,
  // dann einen 5 mS "High-Pulse"

  pinMode(SENSORPIN, OUTPUT);
  digitalWrite(SENSORPIN, LOW);
  delayMicroseconds(2);
  digitalWrite(SENSORPIN, HIGH);
  delayMicroseconds(5);
  digitalWrite(SENSORPIN, LOW);

  // wir benutzen denselben Pin als Eingang und berechnen die Dauer des Pulses zwischen
  // ausgehendem und eingehendem Signal

  pinMode(SENSORPIN, INPUT);
  // wir berechnen die Entfernung aus der Zeitdifferenz
  int nDistance = pulseIn(SENSORPIN, HIGH) / 29 / 2;
  
  nGlobalDistance = (uint8_t)nDistance;

  digitalWrite( LEDREDPIN, ( ( nDistance < 10 ) ? HIGH : LOW ) );
  digitalWrite( LEDGREENPIN, ( ( nDistance >= 10 ) ? HIGH : LOW ) );
  
  delay( 200 );
}

Greetinx
Peter

Was genau heisst "greift nicht"? Was tust du bzw. wie ist der Input, was ist das gewünschte Verhalten, was ist das beobachtete Verhalten? Und wieso glaubst Du, dass das beobachtete Verhalten mit dem IF zu tun hat?

Moin Udo,

solange der Interrupt wie im letzten Code gezeigt aufgebaut ist, wird das Signal des Servos 1:1 durch den Interrupt weitergeleitet. Dies lässt sich verifizieren indem ich Teile der else Klausel auskommentiere. Dann reagiert der Servo nicht mehr.

Somit kann ich sicher sagen dass die Else Klause funktioniert.

Wenn ich den Inhalt der if Klausel auskommentiere ändert sich nichts am Verhalten. Der Servo bewegt sich immer noch analog zu den Knüppelbewegungen, auch wenn ein Hindernis näher als 10cm erkannt wird. Somit ist klar dass die else klausel auch aufgerufen wird wenn der gemessene Abstand unter 10cm liegt. Wenn die if klausel bei einem Abstand unter 10cm arbeiten würde, würde der Servo trotz Knüppelbewegung still stehen, da kein HIGH Signal mehr kommt.

Greetinx Peter

Lass mal nach der Zeile

  nGlobalDistance = (uint8_t)nDistance;

Den Wert von nGlobalDistance mit Serial.print ausgeben. Mal schauen was da wirklich drin steht. Weiterhin würde ich statt

  digitalWrite( LEDREDPIN, ( ( nDistance < 10 ) ? HIGH : LOW ) );
  digitalWrite( LEDGREENPIN, ( ( nDistance >= 10 ) ? HIGH : LOW ) );

eher

  digitalWrite( LEDREDPIN, nGlobalDistance < 10 );
  digitalWrite( LEDGREENPIN, nGlobalDistance >= 10 );

schreiben. Das ist nicht nur lesbarer, Du siehst dann auch was Du wirklich an die ISR übergibst. Was Du im Moment siehst ist nicht der Wert den Du übergibst.

Moin Udo,

hab’s gemacht, gleiches Ergebnis. Der serielle Output zeigt exakt die Distanz in cm an. Die Servoweiterleitung ist aber nach wie vor immer noch aktiv bei Werten unter 10 cm.

Hier ist der geänderte Code.

#include <PinChangeInt.h>

#define INPUTPIN  2

#define LEDREDPIN  4
#define LEDGREENPIN 5

#define SENSORPIN  7
#define OUTPUTPIN  13

volatile uint8_t *pinT_OPort;
volatile uint8_t pinT_Mask, not_pinT_Mask;
volatile uint8_t pintest, pinIntLow, pinIntHigh;

volatile uint16_t  nGlobalDistance;

void quicfunc() 
{
  if ( nGlobalDistance < (uint16_t)10 )
  {
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
   }
  else
  {
    if ( PCintPort::pinState == LOW )
     *pinT_OPort&=not_pinT_Mask;   //  Output to LOW wenn pin auf LOW ist oder wenn ein Hindernis im Weg ist.
    else
     *pinT_OPort|=pinT_Mask;        //  Output to HIGH
  }
}

void setup() {
  Serial.begin( 9600 );
  pinMode(INPUTPIN, INPUT); digitalWrite(INPUTPIN, LOW);
  PCintPort::attachInterrupt(INPUTPIN, &quicfunc, CHANGE);
  
   pinMode(OUTPUTPIN, OUTPUT); digitalWrite(OUTPUTPIN, LOW);
   pinMode(LEDREDPIN, OUTPUT); 
   pinMode(LEDGREENPIN, OUTPUT); 
//   pinMode(LEDYELLOWPIN, OUTPUT);
  // *****************************************************************************
  // set up ports for output ************ 
  // *****************************************************************************
  pinT_OPort=portOutputRegister(digitalPinToPort(OUTPUTPIN)); // output port
  pinT_Mask=digitalPinToBitMask(OUTPUTPIN);                   // mask
  not_pinT_Mask=pinT_Mask^0xFF;                           // not-mask
}

void loop() {

  // wir intialisieren den Sensor, indem wir ihm einen kurzen "Low"-Pulse senden,
  // dann einen 5 mS "High-Pulse"


  pinMode(SENSORPIN, OUTPUT);
  digitalWrite(SENSORPIN, LOW);
  delayMicroseconds(2);
  digitalWrite(SENSORPIN, HIGH);
  delayMicroseconds(5);
  digitalWrite(SENSORPIN, LOW);

  // wir benutzen denselben Pin als Eingang und berechnen die Dauer des Pulses zwischen
  // ausgehendem und eingehendem Signal

  pinMode(SENSORPIN, INPUT);
  // wir berechnen die Entfernung aus der Zeitdifferenz
      
  nGlobalDistance = pulseIn(SENSORPIN, HIGH) / 29 / 2;

  Serial.println( nGlobalDistance );

  digitalWrite( LEDREDPIN, nGlobalDistance < 10 );
  digitalWrite( LEDGREENPIN, nGlobalDistance >= 10 );  
  delay( 20 );

}

Greetinx
Peter

Merkwürdig. Dann bewegen wir uns *langsam vorwärts. Zuerst würde ich ein wenig aufräumen.

  1. In der ISR hat der Vergleich eigentlich nichts zu suchen. Ich würde die Entscheidung aus der ISR herausziehen und nur das Ergebnis übergeben. Das verbessert auch die Performance einen Tick.

Also nicht

if ( nGlobalDistance < (uint16_t)10 )

sondern

if (obstacle_detected)

Dazu wird obstacle_detected als volatile boolean deklariert und in der Hauptschleife mit

obstacle_detected =  nGlobalDistance < (uint16_t)10;

berechnet. Danach kannst Du das auch in der Hauptschleife ausgeben. D.h. ausgeben wie obstacle_detected gefüllt ist.

Weiterhin würde ich

volatile uint8_t pinT_Mask, not_pinT_Mask;

nicht volatile deklarieren. Stattdessen würde ich sie in der ISR deklarieren und zwar als “const”. Direkt in der Deklaration würde ich sie auch füllen. Damit hast Du 2 volatile Variablen weniger am Hals. Das ist immer gut. Da die Variablen “const” sind wird der Compiler das schon so machen, dass die nicht dauernd neu berechnet werden muessen. Wenn Du dem Compiler nicht traust kannst Du ja immer noch “static” statt “const” schreiben, dann hat er keine Wahl mehr.

Wenn das Problem immer noch auftritt, kannst Du dann in der Hauptschleife statt

obstacle_detected =  nGlobalDistance < (uint16_t)10;

auch mal

obstacle_detected =  true;

und

obstacle_detected =  false;

ausprobieren.