PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C INT0 an 328 - (M)Ein Problem



oberallgeier
29.10.2017, 14:39
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 (https://www.roboternetz.de/community/threads/71010-Mikroplatine-zur-Servoansteuerung) defekte Mikroservos wieder zu beleben.

Das ähnliche Projekt funktioniert bestens: im Roboter archie läuft ein Mikrogetriebemotor mit Encoder als Servo (https://www.roboternetz.de/community/threads/61379-Kopfsache-und-ein-m1284-etliche-Servos-viel-Alu?p=625271&viewfull=1#post625271); 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 (http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf) wie dort beschrieben (ATmega328-P_Atmel-42735B_Complete-11-2016, S 89 (http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf)).
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):

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

/* >>
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:

...
// - - - - - - - - - - - - - - -
// 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.

Searcher
29.10.2017, 17:16
Hallo oberallgeier,



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):

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.



In Deinem geposteten Testcode wird ISC00 aber auf 1 gesetzt und damit entgegen dem Kommentar in der gleichen Zeile richtig auf "Any logical change on INT0 generates an interrupt request." gesetzt.



Der ganze Testcode



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


Alle anderen Varianten zeigen (Oskar) entweder keine exakte Kopie der Pulsform, nur 20 ms - Pulse oder gar nichts.

Find ich in Ordnung. Der Testcode tut das was er soll.



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

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?


Möglicherweise ist der Bug in den anderen Projektquellen. Der Testcodeprogrammteil im komplexen Code scheint mir in Ordnung.

Gruß
Searcher

oberallgeier
29.10.2017, 18:58
Grüß Dich Searcher!


.. In Deinem geposteten Testcode wird ISC00 aber auf 1 gesetzt .. richtig auf "Any logical change on INT0 generates an interrupt request." ..Hmmmm. Auaaa. Du hast ja Recht und ich war blind - wohl von der mentalen Art (Leseschwäche hab ich nicht, dachte ich) :-// So ein Mist!


.. Möglicherweise ist der Bug in den anderen Projektquellen ..Noch mehr eigene Blö...Blindheit. Jetzt habe ich statt des (älteren) Files #include "Sbbo2_mot14.c" den aktuellen File #include "Sbbo2_mot16.c" in der Hauptquelle eingetragen, den ich immer fleissig editiert, aber nicht includiert hatte. Nu läufts auch in diesem, eigentlichen Programm. Ich fürchte ich muss mir da über meine Geisteskräfte Gedanken machen.

Ohhh mannnohmannn, danke! War ne Riesenhilfe.

Searcher
30.10.2017, 04:17
Freut mich, daß ich helfen konnte :Strahl Wer kennt das nicht, daß man den Wald vor Bäumen nicht mehr sieht, wenn man schon mitten drin steht?

Gruß
Searcher