Probleme mit dem externen Interrupt INT0.

Aufgabenstellung: der externe Interrupt INT0 soll mit Flankenerkennung "any edge" Start und Ende des Servopulses zeitlich erfassen. Dies wird als Sollgröße in der Regelung weiter verarbeitet.

Das Programm soll auf ATMega328p/8MHz_int-Osz. laufen und über den INT0 die Dauer von Servopuls und Periodenzeit laut dem Protokoll für Modellbauservos bestimmen (1 ms bis 2 ms Puls bei 20 ms Periodendauer). Ziel ist es, etliche defekte Mikroservos wieder zu beleben.

Das ähnliche Projekt funktioniert bestens: im Roboter archie läuft ein Mikrogetriebemotor mit Encoder als Servo; Datenformat ist der übliche Modellbauservo-Puls. Das funktioniert auf einer Controllerplatine babyorangutan seit über einem Jahr gut. Der Motor hat einen Encoder der mit dem gleichen mega328p/20MHz-Resonator vom INT0 ausgelesen wird.

Die neue Variante hat als Stellungsgeber keinen digitalen Eingang (Encoder) sondern einen anlogen Geber, den Servopoti. Daher musste das Programm neu geschrieben werden. Ausserdem ist mir das "alte" Programm (Encoderbasis) zu "heiß gestrickt".

Es gibt Probleme über Probleme mit dem INT0.
A Die Parametrierung des INT0 laut Datenblatt funktioniert nicht wie dort beschrieben (ATmega328-P_Atmel-42735B_Complete-11-2016, S 89).
B Der weitgehend gleiche Code in der späteren Programmumgebung funktioniert nicht (allenfalls/selten wirres Schalten des Ausgangs).

Nach den ersten Programmteilen einschließlich dem Auslesen des Poti (incl. Tests) wurde der INT0 implementiert zum Erfassen der jeweils erfassten Pulsdauer des Servos. Nun zeigte das Programm keine brauchbaren Ergebnisse. Zum Debuggen wurde schließlich ein Port (C0) gewählt, auf dem das mit INT0/PD2 dingelesene Servosignal von der INT0-ISR kopiert wurde. Es kam entweder nichts, oder Wirrwar, jedenfalls keine Kopie des Servopulses an (LED-Anzeige, Oszi-Kontrolle).

Nach stundenlanger Fehlersuche wurde ein Code mit stark gekürzten Variante des Ursprungprogramms geschrieben und auf demselben Testboard für die Programm/Controllerentwicklung getestet (babyorangutan auf Lochraster mit Poti, RS232 und Tastern).

In dieser Testvariante wurde nur der für INT0 relevante Code mit einer Debugfunktion (LED schaltet) geschrieben. Ziel: der am INT0/PD2 erfasste Pegel wird 1:1 an PC0 weitergegeben (siehe unter Tabelle).

Die verlangte Funktion wird NUR erreicht, wenn ISC01 und ISC00 zu Null gesetzt sind, das heißt lt Datenblatt ".. The low level of INT0 generates an interrupt request .."

Auszug aus dem Datenblatt zu mega328p (ATmega328-P_Atmel-42735B_Complete-11-2016, S 89):
Code:
ISC01 . ISC00 . . Description
 .0 . . . 0 . . . The low level of INT0 generates an interrupt request.
 .0 . . . 1 . . . Any logical change on INT0 generates an interrupt request.
 .1 . . . 0 . . . The falling edge of INT0 generates an interrupt request.
 .1 . . . 1 . . . The rising edge of INT0 generates an interrupt request.
Der ganze Testcode
Code:
/* >> 
  Stand ...C1..\C_test1\C_test1.c
 =================================================================================
 Target MCU             : ATmega328p, Testplatine babyorangutan
 Target Hardware        : Babyorangutan
 Target Frequenz        : siehe unten
 =================================================================================
  *** Versionsgeschichte:
 ========================
 x10 28Okt17 1002 Test INT01-Signal=PD2 nach LED rtUserLED=UART-TxD=PD1 ausgeben
 =================================================================================
  *** Aufgabenstellung : LEDTest Blinken im Takt von INT0=Servopuls-Eingang
 ============================================================================== */
  #include <stdlib.h>           //
  #include <avr/io.h>           //
  #include <avr/interrupt.h>    //
  #define       F_CPU   8000000UL
                                //
// FP FP        Funktionsprototypen       FP FP FP FP FP FP FP FP FP FP FP ===== =
  void XTI0_int( void );        // Initialisiere Interrupt 0, PD2, auf ANY edge
  ISR(INT0_vect);               // INT0-PD2 triggert ANY edge => lesen 
  void wms(uint16_t ms);        // Wait für uint16_t Millisekunden
// ============================================================================= =


// ============================================================================= =
// ============================================================================= =
  int main (void)       //
 {                      //
  DDRC |=  (1<<DDC0);   // PC0 => Ausgang rtLEDTest
  DDRC |=  (1<<DDC1);   // PC1 => GND für LEDs
  DDRC |=  (1<<DDC2);   // PC2 => Ausgang gnLEDHeart !Kein Timer für Heartbeat!
  DDRD |=  (1<<DDD1);   // PD1 => Ausgang für user defined LED on babyorangutan
  DDRD &= ~(1<<DDD2);   // PD2/INT0 => Eingang ohne Pullup für Servopuls
  DDRD &= ~(1<<DDD4);   // PD4 => paralleler Eingang ohne Pullup für Servopuls
  PORTC = 0b00000000;   // Keine Pullups, kein gesetzter Ausgang
  PORTD = 0b00010100;   // Pullup am INT0-Eingang und optionalem parallelem Port
// - - - - - - - - - - - -
  XTI0_int ( );
  sei ( );
// - - - - - - - - - - - -
  while ( 1 )
  {
    wms (  500);
    PORTD    |=  (1<<PD1);      //Z appelphilip auf user defined LED
    wms (  500);
    PORTD    &= ~(1<<PD1);
  }
  return 0;
 }                      //        
// =====        Ende des main, der kann aus mehreren Abschnitten bestehen     == =
// ============================================================================= =


// ============================================================================= =
// ===  Initialisierung externer Interrupts 0 bei m328
//      INT0 auf PORTD2 empfaengt ServoPuls
//      Dokumentation ATmega328-P_Atmel-42735B_Complete-11-2016_NEU-NEU..NEU.pdf
// ============================================================================= =
  void XTI0_int( void )            // Initialisiere Interrupt 0, PD2, auf ANY edge
 {                              //
// - - - - - - - - - - - - - - - -
  EIMSK   |=  (1<<INT0);        // erlaube INT0
  EICRA   |=  (1<<ISC00);       // low level of INT0 generates an interrupt request
                                //  =>  im Test triggert   J E D E   Flanke
                                //  Oskar zeigt korrektes Abbild von PD2/INT0
//  EICRA   |=  (1<<ISC01);       // INT0 triggert "auf jede Flanke (any edge)"  S89
                                // ".. Any logical change .."
                                //  =>  im Test NUR   E I N E   Flanke 01? oder 10?
                                //  => denn Oskar zeigt isochrone Pulse (je 20 ms + od -)
//  EICRA   |=  (1<<ISC10);       // falling edge of INT1 generates an interrupt request
                                //  =>  Oskar zeigt im Test sehr unregelmässig
                                //      (mit ms-langen Pausen) inverse Servopulse
//  EICRA   |=  (1<<ISC11);       // rising edge of INT1 generates an interrupt request
                                //  =>  im Test wie eben mit ISC10
 }      // Ende von void XTI_0_init( void )
// ============================================================================= =


// ============================================================================= =
  ISR(INT0_vect)                // INT0-PD2 triggert ANY edge => lesen 
 {                              // => Bei Aufruf PORTD2/INT0 prüfen auf high/low
                                // und high/low weitergeben an Ausgang PC2=LEDTest
  if (((PIND) & (1<<PD2))?1:0)
  { PORTC  |=  (1<<PC0); }
  else
  { PORTC  &= ~(1<<PC0); }
 }                      // Ende ISR(INT0_vect)
// ============================================================================= =


// ============================================================================= =
//### Programm pausieren lassen  !! Der Pausenwert ist nur experimentell !
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  void wms(uint16_t ms)        //
  { 
    for(; ms>0; ms--) 
    { 
//    uint16_t __c = 3000;      // Statt 4000 bei 20 MHz nur 3000 (3200) bei 16MHz
      uint16_t __c = 1600;      //     bzw. statt 4000/20 MHz nur 1600 bei 8MHz
      __asm__ volatile (        //
         "1: sbiw %0,1" "\n\t" 
         "brne 1b" 
         : "=w" (__c) 
         : "0" (__c) 
      ); 
    } 
  } 
// ============================================================================= =
//      E N D E         E N D E
// ============================================================================= =
Alle anderen Varianten zeigen (Oskar) entweder keine exakte Kopie der Pulsform, nur 20 ms - Pulse oder gar nichts.

Derselbe Code in den komplex(er)en Projektquellen führt zu keiner Ausgabe der auf INT0 eingelesenen Daten.

Die relevanten Teile des Originalen Codes:
Code:
...
// - - - - - - - - - - - - - - -
// Ports als Ein- (0) oder Ausgänge (1) konfigurieren, Pins/Pull Ups (1) aktiv.
//   A = Ausgang, E = Eingang ohne , EU = Eingang MIT PullUp, Belegung siehe oben
  DDRB  = 0b00000111;   // Mot1/2: PB1 = OC1A-MotorPWM; PB2 = OC1B-MotorPWM
  PORTB = 0b11111000;   // 
                        //
  DDRC  = 0b01101111;   // PC6+7-Pin  NUR bei babyorangutan (TQFP+MLF)
  PORTC = 0b10100000;   // PC0=LHeart/~beat, PC1=GND, PC2=LEDTst
                        // PC3=GND, PC4-ADC/Srv-Poti ohne Pullup, PC5=VccPoti!!
                        // 
  DDRD  = 0b00000011;   // RX/TX-PD0/1, PD2 = INT0-Servopuls,
                        // PD5/PD6 = M1B/M1A, PD7 = Taste: PullUP !
  PORTD = 0b11110100;   //   Pull Ups aktivieren, mit pololuMot AUCH bei INT0/~1
                        //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
...
...
  XTI0_int ( );                 // Initialisiere Interrupt 0, PD2, auf ANY edge
...
                        //
// ============================================================================= =
  sei();                // ###  Globalen Interrupt freigeben <<<<<#####
// ============================================================================= =
...
// ============================================================================= =
// ===  Initialisierung externer Interrupts 0 bei m328, doc 8271I
//      INT0 auf PORTD2  --  ServoPuls auf PIND2
//      Dokumentation ATmega328-P_Atmel-42735B_Complete-11-2016_NEU-NEU..NEU.pdf
// ============================================================================= =
  void XTI0_int( void )            // Initialisiere Interrupt 0, PD2, auf ANY edge
 {                              //  => EICRA ISC00 + ~10 auf 1                 S89
// - - - - - - - - - - - - - - - -
  EIMSK        |=  (1<<INT0);   // erlaube INT0
  EICRA        |=  (1<<ISC00);  // INT0 triggert auf jede Flanke (any edge) ?? S89
//EICRA        |=  (1<<ISC01);  // INT0 triggert auf jede Flanke (any edge)    S89
  IZTtmrA       =       555;    // Timer f Stoppen Servopuls; läuft wenn PD2 high
  tPLShi        =       555;    //
  tPLSlo        =       555;    //
                                //
 }      // Ende von void XTI_0_init( void )
// ============================================================================= =


// ============================================================================= =
// ===  Nicht unterbrechbare ISR für EXT_INT0{any edge} auf mega328      ======== =
//      Aufruf bei Signalumschlag des Servo-Datenkanals.
//      Prüfung Pulsanfang oder -ende ?
//      Pulsanfang:     <=> PD2/INT0 ist high
//                      setze IZTmrA zurück auf 0 (Null)
//                      speichern TCNT1 (max 1023) nach s16 Pulsan
//      PulsEnde:       <=> PD2/INT0 ist low
//                       => speichern Diff TCNT1 - Pulsan nach Pulshi
//                       => speichern IZTmrA
//              #############################################
//  cli(); A3ist = TCNT1; sei();  // Nicht-atomarer Wert TCNT1 ! ! !, max 1023 !!!
//      ##>     Richtungskonform prüfen ! ! ! ? ? ?
// ============================================================================= =
// ============================================================================= =
  ISR(INT0_vect)                // INT0-PD2 triggert ANY edge => lesen 
 {                              //   => Bei Aufruf PORTD2 prüfen auf high/low 
  if (((PIND) & (1<<PD2))?1:0)
  { PORTC  |=  (1<<PC0); }
  else
  { PORTC  &= ~(1<<PC0); }
 }      // Ende ISR(INT0_vect)
// ============================================================================= =
Die vorgestellten Probleme laufen mit 20MHz/Resonator ebenso wie mit 8 MHz/int-Oszillator.

Fragen
Hat bitte jemand eine Idee was da falsch läuft?
Kann mir bitte jemand einen Rat geben wo ich noch nach Fehlern suchen kann?

Nachtrag:
Bitte um Nachsicht, dass ich sooo viel schreiben muss - müsste ja alles gelesen werden.