![]() |
AVR-Interrupts in C
Habe mich mal wieder an die Elektronik gesetzt und Promt das nächste Problem:
AVR-Studio, ATmega8535 Ich möchte ein kleines Versuchsprogramm haben, dass alle 5000Takte den PortB um einen runterzählt und bei PIND2 per Interrupt PORTB auf 0x00 setzt. Klappt natürlich nicht, ich denke ich habe die Geschichte mit den Interrupts noch nicht 100%ig verstanden, werde aus diversen Tutorials auch nicht wirklich schlau was jetzt genau wo hin muss. Hier mal mein Text ----------------------------------------- #include <AVR/io.h> #include <avr/interrupt.h> int main (void) { int i; DDRB=0xff; PORTB=0xff; GIFR=(1<<INTF0); MCUCR =((1<<ISC01)|(1<<ISC00)); sei(); while(1) { while(i<5000) i++; PORTB--; i=0; SIGNAL (SIG_INT0) { PORTB=0; } } return(0); } ----------------------------------------- Was mach ich falsch? |
Versteh ich das richtig das du Port B bei einem externen Interrupt an Pin D2 auf 0 setzen willst?
Grüße Bastian |
Wie ist denn der INT0 extern beschalten (Taster/Pullup?) bzw. wie sind den die Pegel an dem Pin so?
Die Interrupt-Routine würde ich übrigens aus dem main rausverfrachten, villeicht mag das dein compiler so nicht... lg |
Habe es bis jetzt nur im AVRstudio simuliert.
Das Prog soll von sich aus Runterzählen(an PortB) und ihn auf Interrupt von PIND2 auf 0 setzen. Ich verstehe den Syntax der Interrupts noch nicht so ganz. Vllt könnte jmd meinen Text exemplarisch mal so umstellen wie es funktionieren müsste... LG |
Hallo,
// Beispiel für einen Timer Interrupt: int cnt = 0; int main( void ) { TIMSK |= BV(TOIE0); // Enable timer interrupt sei(); // Enable global interrupt flag for (;;); // Loop forever oder mache hier irgendwas } // Diese Funktion wird bei overrun timer counter aufgerufen SIGNAL (SIG_OVERFLOW0) { PORTB = ++cnt; } Das geht mit allen anderen Interrupts ähnlich. 1. Das richtige enable bit setzen 2. Die SIGNAL oder INTERRUPT Fumktion schreiben SIG_ ist für jeden Interupt unterschiedlich deklariert. Beachten: bei vielen Ints muss das dazugehörende Register gelesen werden (RTFM) Bei verwendung von INTERRUPT anstatt SIGNAL können verschachtelte Interrupts auftreten. Fang mit SIGNAL an und langsam rantasten, dann wird das schon gehen. Viel Erfolg Dirk |
Hi Dirk,
Zitat:
Welche Register muss ich wie und mit welcher Syntax setzen? GIFR=(1<<INTF0) bedeutet doch, im Global Interrupt Flag Register den INTF0 auf 1 setzen oder? EDIT: mit Zitat:
|
// EDIT:
klar, das Bit ist wichtig - "External Interrupt Request 0 Enable"... ohne dem geht natürlich gar nichts ;) lg Chris |
Oh man, diese Controller rauben mir noch den letzten Nerv, hab grad das Gefühl, je mehr ich darüber lese und damit mache, desto weniger verstehe ich letztendlich...
Ich habe jetzt noch einen 2.Externen Interrupt drin, und jedesmal wenn der aufgerufen wird, steigt der Prozess danach wieder ganz oben im MAIN ein. Kann ich ihn auch irgendwie dazu bringen, in der Hauptschleife zu bleiben? |
Hallo,
soweit bist du garnicht weg vom Ziel. Gestatte mir noch folgende Korrekturen: 1. PORTB--; geht nicht, weil ein decrement aus lesen und schreiben besteht. Lesen eines Portes muss immer mit PINB erfolgen. Also: PORTB = PINB-1; 2. Wie willst du den Erfolg deiner Programmierung kontrollieren ? Baue ein,zwei LED ein und setze sie bei einem Event. Damit debugged es sich leichter. Last not least: ich kann an deinem Programm keinen Sinn erkennen. Ist das nur eine Übung, oder übersehe ich was ? Gruss Dirk |
Das is erstmal eine Übung.
Ich kann den Erfolg auf dem STK500 sehen, wo der gesamte Portb an den 8 LEDs hängt und einfach binär durchzählt. Das Program soll wenns fertig ist, über INT1 eine "run-Variable" schalten, wenn die an ist soll er zählen und sich per INT0 resetten lassen. PORTB-- funktioniert übrigens (Durch das DDRB hab ich ja festgelegt, dass PORTB immer den Wert von PINB annimmt), das is nich das Problem, nur diese Interrupt-Steuerung raff ich nicht, also wann und wo genau ich jetzt was wie setzen/lesen muss damits läuft... |
Kann mir jmd erklären, warum der Programmzeiger in der Simulation nach jedem Interrupt wieder oben in der 1.Zeile des Main-Prog einsteigt ohne den SIG_Befehl auszuführen?
Eigentlich sollte er doch in der Hauptschleife bleiben, dann das SIG_... ausführen und danach wieder in die Hauptschleife zurückspringen oder? |
Hi,
der Effekt ist typisch für ATMELs, wenn du den falschen Interruptnamen programmierst. Der C-Compiler erstellt am Anfang des SRAM ein Sprungbefehl nach main, gefolgt von einer Vektortabelle, in der zu jedem Int ein Interruptfunktionszeiger stehen. Wenn du eine Interruptfunktion für den externen INT0 interrupt schreibst, belegt der C-Compiler den dafür vorgesehenen Slot. Der Interrupt wird ausgelöst, der Prozessor holt den Funktions zeiger und jumped dahin. Wenn jedoch da ne NULL steht, weil es keine Funktion dafür gibt, jumped das Programm nach 0 und das ist der Start des Programms. Du musst also deine Namen SIG_INT0 kontrollieren. Gibt es die bei dem verwendeten Prozessor überhaupt ? Sind in deinem Programm mehrere Interrupts enabled ? Gruss Dirk |
Also die SIG_INT0 gibt es und hab ich weiter oben, ausserhalb vom Main definiert.
Ich habe INT0 und INT1 enabled. Hier mal der Neue Text... Zitat:
|
Setz den MCUCR vor GICR.
Im Manual steht ein Hinweis: When changing the ISC2 bit, an interrupt can occur. Therefore, it is recommended to first disable INT2 by clearing its Interrupt Enable bit in the GICR Register. Then, the ISC2 bit can be changed. MCUCR = ((1<<ISC01)|(1<<ISC00)|(1<<ISC11)|(1<<ISC10)); GICR = ((1<<INT0) |(1<<INT1)); // das brauchst du wahrscheinlich nicht: GIFR = ((1<<INTF0)|(1<<INTF1)); Viel Erfolg Dirk |
Hi Dirk, ich benutze doch aber INT2 gar nicht, sondern nur INT0 und INT1...
|
Hallo,
probier das doch mal. Vieeleicht gilt das ja auch für die anderen beiden INTs. Irgendwie wird das schon gehen. Haben andere ja auch schon hinbekommen. Gruss Dirk |
Hm, keine �nderung.
Bei Wechsel von 0 nach 1 an PIND2 oder PIND3 Springt der Zeiger an die erste Stelle des Main-Blocks ohne jedoch sie SIG-Routine auszuf�hren, dh, die Variable "run" wird nicht ver�ndert... EDIT: Ok, langsam dämmert es mit dem SIG_... Aber wo finde ich den namen des entsprechenden Vektors, bzw wie setzt er sich zusammen? Im Manual steht Vector No.2 bzw 3, 0x001/0x002, INT0/INT1, External Interrupt Request0/1. Wie setzt sich das mit dem SIG zusammen? EDIT2: ISR(_VECTOR(1)){run=0;} ISR(_VECTOR(2)){run=1;} funzt, zufällig eben per Google gefunden. Wie hätte ich jetzt darauf kommen können? |
Hallo,
in den .h Dateien stehen die Deklarationen. Für deinen 8535 gilt: #define SIG_INTERRUPT0 _VECTOR(1) #define SIG_INTERRUPT1 _VECTOR(2) #define SIG_OUTPUT_COMPARE2 _VECTOR(3) #define SIG_OVERFLOW2 _VECTOR(4) #define SIG_INPUT_CAPTURE1 _VECTOR(5) #define SIG_OUTPUT_COMPARE1A _VECTOR(6) #define SIG_OUTPUT_COMPARE1B _VECTOR(7) #define SIG_OVERFLOW1 _VECTOR(8) #define SIG_OVERFLOW0 _VECTOR(9) #define SIG_SPI _VECTOR(10) #define SIG_UART_RECV _VECTOR(11) #define SIG_UART_DATA _VECTOR(12) #define SIG_UART_TRANS _VECTOR(13) #define SIG_ADC _VECTOR(14) #define SIG_EEPROM_READY _VECTOR(15) #define SIG_COMPARATOR _VECTOR(16) Es handelt sich um direkte Adressen im RAM. _VECTOR(0) ist der Sprung nach main() und die anderen zeigen auf die jeweilige Behandlungsroutine. Wenn du eine Behandlungsroutine schreibst, fügt der Compiler in den passenden Slot der Tabelle die Adresse zur Funktion. Wenn du programmierst: SIGNAL(SIG_INTERRUPT0) {} steht nach compilieren im vector 1 ein Zeiger auf deine Funktion. Definiere doch mal zu allen möglichen 15 Interrupts eine SIGNAL Funktion, dann wird das Programm irgendwo hinspringen, nur nicht auf 0. Viel Erfolg Dirk |
Hi Dirk, ok, jetzt hats geklickt, und funktioniert.
Vllt noch ne etwas peinliche Frage: wo finde ich denn die *.h-files? finde im Atmel-Ordner keine und würde da ganz gerne mal reinschauen. Könnte vllt noch ganz aufschlussreich sein... |
Hallo,
bei mir liegt das unter c:\winavr\avr\include\avr Aber verrate mir jetzt mal, was letztendlich der Grund war, der das Programm auf Adresse Null geführt hat ? Gruss Dirk |
Der Grund war ganz einfach der, dass ich wie du schon erkannt hattest, die SIGNAL(SIG_INT0) benutzt habe, die der controller gar nicht kennt, denn sie heisst eben
SR(_VECTOR(1)) Wenn die die io.h im WINAVR-Verzeichnis zu finden ist, wie kann dann AVR-Studio darauf zugreifen? Oder gehören die Progs zusammen (weiss garnicht mehr ob ich die zs installiert habe oder einzeln) Ich arbeite nur mitm AVR-Studio weil die Simulation damit eben möglich ist... EDIT: In der m8535.h stehen dann wiederrum Folgende Bezeichner: Zitat:
|
WinAVR ist eine avr-gcc-Distribution, die als Plug-In im AVR Studio arbeitet und das C-programmiern dort erst möglich macht.
Und wie du schon erkannt hast, die Interrupt-Routinen können entweder mit ISR (INT0_vect) oder ISR (SIG_INTERRUPT0) oder eben 'direkt' (ohne den Umweg über ein Define) mit ISR (_VECTOR(1)) bezeichnet werden... durch die Defines muss man aber den Vektor nicht manuell anpassen, wenn man das Programm auf einem anderen Controller verwenden will. lg |
Ok, "klick" häts ´gmacht ;)
Als nächstes werde ich mich mal mit dem 2-Wire-Serial und mit USB-Steinen beschäftigen, dann hab ich alles zusammen was ich für diverse Panels brauche, fängt langsam richtig an Spass zu machen. btw: hab HIER (Anfang unteres Drittel der Seite) was gefunden zur Drehrichtungserkennung von Phasenverschobenen Drehencodern. Der Beitrag mit dem "Greycode", hat das mal jemand versucht? ich verstehe da die super-kurz-kompakt C-Befehle nicht, zb Zitat:
Mir ist auhc das Prinzip noch nicht ganz klar wie das gehn soll, aber es ist auf jeden Fall wenn ich es zum laufen kriege deutlich kürzer als meine bisherige REDEC-Programmierung... |
Die Auswertung von Greycode-Encodern erfolg im Prinzip durch periodisches Einlesen der zwei Phasen und vergleichen mit dem vorherigen Zustand... je nach Ergebnis dieses Vergleiches kann man dann auf die Drehrichtung rückschließen. Den c't-Artikel mag ich jetzt nicht lesen, aber ich vermut mal, dass wird dort eh sehr ausführlich erklärt ;)
Zum C-Code... i += 2; bedeutet nix anderes als i = i + 2; das gleiche gilt natärlich auch für andere Operanden (wie eben "&", "|", "-" usw) Und das "~" invertiert den darauf folgenden Ausdruck bitweise. lg |
Habe den Graycode mal Zeile für Zeile auf Papier durchlaufen und komme zu folgendem Problem: Wenn ich langsam drehe, und in der 11-Position (beide Flanken high) mehrere Takte durchlaufen, dann Zählt er doch pro Takt um einen hoch.
Dazu noch das Problem dass dieses Beispiel pro Raste um 2 hochzählt (laut Beschreibung, ich komme sogar teilweise auf 3), was mache ich nun wenn ich um je einen hochzählen möchte? |
Aahhaaaa, jetzt ises klar:
Der Kern liegt in dem Zitat:
Dadurch entsteht ein vierstelliger Code mit zwei Stellen aktueller und zwei Stellen alter Wert. In der Tabelle staht dann für jede möglich Vorher-Nachher-Kombination die entsprechende Reaktion. Wenn ich jetzt pro Raste nur um einen hoch- oder runterzählen will, dann muss in der Tabelle einfach überall eine Null stehen und nur bei zb "vorher 00 und dann 01"-> wert++ und bei "vorher 00 und dann 10"-> wert-- Is ja total genial und wirklich deutlich kürzer und schneller als meine "REDEC" und wenn ich es richtig sehe entfällt damit sogar ein entprellen, oder? |
Hallo erstmal,
Zitat:
Und noch ein Hinweis zu den Interrupts, Variablen die im Interrupt geaendert werden solltest du als volatile deklarieren. Statt "int i;" einfach "volatile int i;" schreiben. So zwingt man den Compiler vor jedem Lesezugriff auf die Variable den aktuellen Wert aus dem Speicher zu lesen und bei jedem Schreibzugriff den Wert in den Speicher zurueckzuschreiben, so verhindert man das der Compiler zu sehr optimiert. Kleines Beispiel: Code:
int main(void) Wenn nun ein Interrupt eintritt der wie folgt aussieht Code:
ISR(TIMER1_COMPA_vect) Hoffe das war ein wenig verstaendlich. Mehr Informationen gibts zB hier: http://www.mikrocontroller.net/artic...R-GCC-Tutorial |
Zitat:
Sonst macht die ganze Überlegung keinen Sinn... Mit dem volatile ist nach deine super Erklärung nun auch klar. Es geht voran. Jetzt fehlen eben nur noch dach 2wire-Serial und die USB-Vernetzung. Dazu werd ich Euch dann sicher demnächst wieder löchern... |
Hm, wisst Ihr welche *.h ich noch includen muss damit die Befehle sbi und cbi funktionieren? (set/clear bit)
Mit io.h gehts erkennt er die nicht... |
Die Funktionen sbi/cbi finden sich nur mehr in alten avr-libc Versionen... heutzutage verwendet man diese Funktionen nicht mehr.
Stattdessen werden einzelne Bits sehr übersichtlich und ISO-C konform so gesetzt: REG |= (1<<BIT); z.B. PORTB |= (1<<PB2); und so gelöscht REG &= ~(1<<BIT); also z.B. PORTB &= ~(1<<PB2); Das ist zwar etwas mehr Schreibarbeit, kann aber im späteren Entwicklungsstadium und bei größeren Programmen wirklich seeeeeeehr hilfreich sein. lg |
Danke
|
Mal wieder was.
Wenn ich den Atmega8535 jetzt ausserhalb des STK500 auf der Platine selbst einsetzen will, was gibts da zu beachten? Werde ihn nicht direkt verlöten sondern auf einen Sockel stecken (Bauweise PDIP). Also welche Pins MÜSSEN auf Masse, braucht es zwingend ein externes Quarz, muss ich die Versorgungsspannung von einem Labornetzteil per Kondensator glätten? Wenn ja, wie sollte der dimensioniert sein etc. |
Alle Zeitangaben in WEZ +2. Es ist jetzt 23:36 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
© 2009 FSL Verlag