Timer/ Interrupt basierte IR Analyse

Hallo!
Ich habe vor dieses Projekt (Infrarot-Schalter und -Regler) zum Regeln der Lautstärke eines Audiosignals als Arduinoprojekt nachzubauen. Der Programmablauf ist unten erklärt und ich bin dabei, ihn mehr oder weniger auf die Processing Umgebung zu übertragen (soweit das eben von einer strukturierten auf eine objektorientierte Programmiersprache möglich ist). Zum Umsetzen der IR Signalanalyse wie im Assemblercode versuchte ich eben auch mit der Kombination aus Timer und PinChangeInterrupt zu arbeiten, wobei mir gleich ein Thread auffiel an dem ich mich stark orientierte, den ich jedoch nicht zu 100% durchschaute (http://arduino.cc/playground/Code/ReadReceiver). Diese Vorgehensweise erschien mir auch aufgrund der Interrupts exakter als Vorgehensweisen wie diese → http://learn.adafruit.com/ir-sensor/reading-ir-commands auf die ich später stieß. Jetzt habe ich meinen Code zur Analyse der IR Signale endlich fertig und habe nötige Änderungen bei der Übertragung des Beispielthreads “ReadReceiver” auf mein Projekt angepasst (#include <PinChangeIntConfig.h> fällt in der neuen Version weg und mit ihm die defines, nur ein Receiverpin also auch keine for-Schleife im setup und statt pin eine Pinvariable und (zur besseren Verständlichkeit andere Namen der Interrupt-Methoden)).
Der Code konnte auch ohne Fehlermeldung des Compilers hochgeladen werden, jedoch kam beim Öffnen des Serial Monitors und der korrekten Initialisierung der Baudrate die Ernüchterung: Es wurden nicht wie erhofft die Timerzeiten von Low bis erneut Low ausgegeben, als ich mit meiner IR Fernbedienung auf den Receiver TSOP 1738 beim Drücken eines Knopfes zielte, sondern nur die Systemausgabe “PinChangeInt test am IR Pin 11”. Der Funktionstest des Receivers war jedoch positiv und als ich sämtliche Instruktionen im Code nach jeder Zeile mit einer Systemausgabe versah, gingen die Systemausgaben erstaunlicherweise nur bis zur Instruktion Timer1.restart() also noch in der setup(). Jetzt verstehe ich überhaupt nicht, was nicht stimmt, obwohl mir gleich aufgefallen war, dass .restart im Gegensatz zu .stop nicht gehightlighted wurde.
Daher frage ich mich:
- Als Keyword findet sich .restart in der include Datei TimerOne.h, warum wird dann weder gehighlighted, noch fortgefahren?
- Warum stehen die Anweisungen Serial.print(‘f’) bzw. Serial.print(‘r’) als Kommentare, falls falsch, was nützen sie wenn in der loop() durch Serial.read nach ‘p’ gesucht wird?
- Muss der state beim case FALLING nicht schließlich 0 sein?
(- Ist i bei pin nicht ausschließlich eine Zählvariable für die for Schleife und im weiteren Code überhaupt verwendbar?) In meinem Fall egal, da ich ja nur einen relevanten Eingang habe…
```
*#include <PinChangeInt.h> //PinChangeInterrupt-Include Bibliothek
#include <TimerOne.h> //Timer1- Include Bibliothek

int pbIrIn = 11; //portbIRinput IR-Receiver am Digitaleingang 11

byte burp=0; //Zählvariable für die Anzahl der PinChangeInterrupts/PCInts/IR Signale ankommend + aufhörend
byte IRstate=0; //IR Status, RISING/ansteigend/Signal kommt an, oder FALLING/fallend/Signal hört auf
byte cmd=0; //Speicher für Serielle Daten
unsigned int time=0;

void setup()
{
//Pin-Modus Konfiguration 
  pinMode (pbIrIn, INPUT);
 
  digitalWrite(pbIrIn, HIGH);
 
  Serial.begin(115200); // Serielle Kommunikation bei Übertragungsgeschwindigkeit von 115200 Baud
   
  Serial.print("PinChangeInt test am IR Pin "); // Funktionstest der PinChangeInt.h Include Datei
  Serial.print(pbIrIn); // funktioniert, wenn Pin 3/11? ausgegeben wird wie im define initiiert
  Serial.println(); // neue Zeile
 
  Timer1.initialize(2200); // Längster PWM-Impuls ist normalerweise 2,1ms lang, mit etwas Spielraum gewählt
  Serial.println(“Timer1 initialisiert”);
  Timer1.stop(); // Timer stoppen…
  Serial.println(“Timer1 gestopppt”);
  Timer1.restart(); // …und zurückksetzen, um direkt beim ersten Interrupt zu starten
  Serial.println(“Timer1 neu gestartet”);
  PCintPort::attachInterrupt(pbIrIn,IRSignalerkannt,RISING); //Interruptfunktion “IRSignalerkannt” wird bei steigender Spannung am PIN (3/11) ausgeführt
  Serial.println(“Interrupt attached”);
}

void loop()
{
cmd=Serial.read();
if (cmd==‘p’)
{
  Serial.print(“time:\t”);
  Serial.print(pbIrIn);
  Serial.print(": “);
  Serial.print(time,DEC);
  Serial.print(”\t");
 
  Serial.println(burp, DEC);
  Serial.println();
}
cmd=0;
switch (IRstate)
{
  case RISING:
    PCintPort::detachInterrupt(pbIrIn);
    PCintPort::attachInterrupt(pbIrIn, IRSignalstoppt, FALLING);
    IRstate=255;
    break;
  case FALLING:
    PCintPort::detachInterrupt(pbIrIn);
    PCintPort::attachInterrupt(pbIrIn, IRSignalerkannt, RISING);
    IRstate=255;
    break;
}
}

void IRSignalerkannt() //Pin_change interrupt am IR-Eingang (11), startet Timer um Signaldauer zu identifizieren
{
  Timer1.restart(); //Timer wird zurückgesetzt…
  Timer1.start(); //… und gestartet
  IRstate=RISING;
  Serial.print(“Der erste Interrupt wurde ausgelöst”);
  // Serial.print(‘r’); - ?wird im Beispielsketch nur als Kommentar eingeschrieben ?
  burp++;
}

void IRSignalstoppt ()
{
  IRstate=FALLING;
  time=readTimer1();
  //Serial.print(‘f’);
  Timer1.stop();
}

unsigned long readTimer1()        //gibt den Timerstand in Mikrosekunden aus
{                                   
    unsigned int tmp=TCNT1;
    char scale=0;
    switch (Timer1.clockSelectBits)
    {
    case 1:// no prescalse
        scale=0;
        break;
    case 2:// x8 prescale
        scale=3;
        break;
    case 3:// x64
        scale=6;
        break;
    case 4:// x256
        scale=8;
        break;
    case 5:// x1024
        scale=10;
        break;
    }
    while (TCNT1==tmp) //if the timer has not ticked yet
    {
        //do nothing – max delay here is ~1023 cycles
    }
    tmp = (  (TCNT1>tmp) ? (tmp) : (ICR1-TCNT1)+ICR1  );//Wenn heruntergezählt wird, füge den obigen Wert hinzu
    return ((tmp1000L)/(F_CPU /1000L))<<scale;
}

```
Schon im Vorraus vielen Dank für eure Hilfe! :slight_smile:

Als erstes würde ich mal die ganzen serial.print geschichten aus dem interrupt code entfernen und durch das blinken einer LED ersetzen. Dann verpasse dir selbst einen klaps auf den hinterkopf.

Das senden über serial wird ab 1.01 interrupt-basiert erledigt und kann somit innerhalb eines anderen interrupts nicht funktionieren.

stimmt das war nicht überlegt ;) trotzdem sind serial.print befehle ja innerhalb des setups problemlos verwendbar.. wieso hört die programmabarbeitung dann plötzlich bei Timer1.restart auf? Und wie stellst du dir das mit der blinkeden led vor, die serial.print befehle, die nicht -komischerweise nur als kommentar, was sich für mich ohnehin nicht erschließt- vom beispielsketch übernommen wurden haben ja auch keine bedeutung für den ablauf des programms, sondern wurden von mir nur eingefügt, um zu erkennen wo der wurm drin ist .. Du bist mir glaub ich mehr als einen Schritt voraus :D

Da hilft es nur systematisch vorgehen ;-) Alles rauswerfen / auskommentieren und schrittweise testen, bis es haengt.

Übrigens kann man auch sehr gut mit der normalen 'Uhr' des Arduino im niedrigen µs bereich messen [millis() bzw. micros()]. Ein bischen jitter bleibt über, aber für eine IR fernbedienung reicht es allemal.

Es ist schon sehr überwältigend, wieviel c++ code geschrieben werden muss, um ein bischen pin-change interrupt in eine bibliothek zu packen. Gut, es wird vermutlich portabler, ist in eine klasse gepfercht und so weiter, aber eigentlich braucht man nur ein paar register zu setzen...