Hallo,
Bist Du auf die Idee gekommen mal io.h von Deinem µC zu lesen ?
Wenn nicht dann schau mal rein
Gruß Sebastian
Guten Tag Leute!
Hab mich lange um die Interrupts gedrückt, aber irgendwie will ichs heut
wissen, und die Dinger nun endlich angehn.
Es würde mich freuen, wenn hier ein kleines aber feines Tutorial entsteht. Betonung liegt auf klein und fein.
Zunaechst mal wo liegt ueberhaupt mein Problem/was ist mein Stand?:
Hab nun so weit im Atmega8 Datenblatt gewühlt und folgendes zusammengetragen:
Auch die Intterruptvektortabelle des Atmega 8 (auf Seite 44) ist mir ins Auge gestochen. Gut so weit. Was ich nun aber nicht gefunden habe, istCode://Wissenswertes zu Interrupts auf AVRs: // Global Interrupt Enable I-Bit im GICR Register ist normalerweise LOW wenn Interupt ausgeführt wird // Interrupts haben zwar eine Priorität, können sich aber normalerweise nicht gegenseitig unterbrechen // Interrupt Schachtelung mittels manuellem I-Bit setzen möglich // Information im Statusregister SREG geht bei Interruptausführung verloren wenn man nicht sorgetraegt // Befehle: _SEI() = Set I Bit - Aktiviert Interruptfaehigkeit // _CLI()= Clear I Bit - Schaltet Interruptfaehigkeit aus // _SLEEP() = uC geht in den Sleep Modus und wartet auf naechsten Interrupt
wo /wie schreibe ich nun eine ISR für einen Interrupt???
Prinzipiell sollte es ja so gehen wie bei folgendem Programmbeispiel - einem RS232 Mirror:
Aber woher weiß ich bzw. der Verfasser dieses Codes das der Interrupt ausgerechnet SIG_UART_RECV heißt? Dazu habe ich weder im Datenblatt noch sonst wo was gefunden.Code:#include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #define SYSCLK 7372800 #define BAUD 38400UL #define UBRR_BAUD ((SYSCLK/(16*BAUD))-1) /* USART initialisieren */ void uart_init(void); int main(void) { /* USART initialisieren */ uart_init(); sei(); /* Nichts tun. Die Interrupts erledigten den Rest */ while (1) ; } void uart_init(void) { /* Baudrate einstellen ( Normaler Modus ) */ UBRRH = (unsigned char) (UBRR_BAUD>>8); UBRRL = (unsigned char) UBRR_BAUD; /* Aktivieren des Empfängers, des Senders und des "Daten empfangen"-Interrupts */ UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN); /* Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit */ UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); } /* Interrupt wird ausgelöst sobald neue Daten im USART-Empfangspuffer liegen */ SIGNAL(SIG_UART_RECV) { unsigned char buffer; /* Daten aus dem Puffer lesen ... */ buffer = UDR; /* ... warten bis der Sendepuffer leer ist ... */ while ( !( UCSRA & (1<<UDRE)) ) ; /* ... und gleich wieder zurück schicken */ UDR = buffer; }
Wär super, wenn mir da jemand auf die Sprünge helfen könnte!
Vielen Dank schon mal im Voraus !
uC
Hallo,
Bist Du auf die Idee gekommen mal io.h von Deinem µC zu lesen ?
Wenn nicht dann schau mal rein
Gruß Sebastian
Linus TorvaldSoftware is like s e x: its better when its free.
Waer der Abschnitt der iom8.h die in die io.h eingebunden im Falle eines Atmega8 eigebunden wird. Danke für den spartanischen aber brauchbaren Tipp.Code:/* Interrupt vectors */ #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) #define SIG_2WIRE_SERIAL _VECTOR(17) #define SIG_SPM_READY _VECTOR(18) #define _VECTORS_SIZE 38
Zum Gesamtverstaendnis und zur Eigenkontrolle: Es sind Hardwaremaessig Flags reserviert, welche ueber ein entsprechendes
Ereigniss informieren (z.B. Int0, Toggle an Pin PD2), sobald aktueller
Befehl abgearbeitet ist UND das IVCE-Bit im GICR Register ist Null sowie das Bit 7 (berühmtes I-Bit ) in SREG gesetzt ist.
Danke für die Hilfe! Den Blick in die io.h hät ich wirklich eher waagen sollen...
uC
Noch ne Frage, beim durchblaettern, hab ich die Anweisung _SFR_IO8(0x3A)
gefunden. Ist das der Zauberbefehl, mit dem man Zeigerbiegen kann? Sprich
Einen Zeiger auf eine Speicherstelle definieren kann?
Danke wieder im Voraus
Ja, das würde ich auch immer empfehlen, ist auch der einfachste WegDen Blick in die io.h hät ich wirklich eher waagen sollen...
Zu Deiner anderen Frage, es ist ziemlich einfach zu erklären, die Vektoren liegen alle ganz am Anfang von Flash, bei Adresse 0 reset, also die Stelle, wo der µC immer anfängt, Adresse 1 externer Interrupt INT0 usw. wie in der io8.h aufgelistet.
Wenn man das jetzt im Assembler schreibt sollte man die ganze Tabelle am Anfang ausschreiben z.B.
[code]
org 0x0000
rjmp reset
rjmp siginterrupt0
reti
reti
...
[code]
Wenn jetzt ein Ereignis eintrifft wird wie Du schon sagtest ein Flag gesetzt
hier z.B Register GIFR Bit INTF0.
Wenn jetzt Interrupts erlaubt sind (sei) und hier Register GICR bit INT0 wird zu Adresse 0x01 gesprungen, wo wiederum rjmp siginterrupt0
steht, womit Du dann irgendwo im label siginterrupt0 landest.
Andere Interrupts stoßen da direkt auf reti, kommen also sofort wieder zurück.
Die Vectortabelle ist stark µC abhängig!
also immer nachgucken wie sie heißen, der Kompiler prüft die Namen nicht
und wenn Du Dich vertippt hast klappt die ganze Sache nicht...
Ich hoffe das ist verständig genug, und alles so 100% richtig.
P.S. Welche avr-gcc Version hast Du eigentlich, ist aber nicht die neueste
man sollte nicht mehr mit SIG..... arbeiten, ich würde mal updaten sehe hier
Gruß Sebastian
Linus TorvaldSoftware is like s e x: its better when its free.
Noch eine Frage: Welche Rolle spielen die Interrupt.h und die Signal.h
signal.h gilt als veraltet (avr 3.4.4) und wird irgendwann aus avr-gcc raufliegen, man soll nur die interrupt.h benutzen.
Gruß Sebastian
Linus TorvaldSoftware is like s e x: its better when its free.
Hi, habe nun also ein wenig mit den Interrupts gespielt. Im Anhang ein nettes kleines Demoprogramm, dass beide externen Interrupts bedient.
Kurzbeschreibung: Wird am Int0 Pin eine steigende Flanke produziert,
so wird Zustand des PortsB um 1 erhöht.
Wird INT1 ausgeloest, wird PortB einfach 0x0a aufgeprägt.
Funktioniert so weit ganz gut, nur ein paar Schönheitsfehler die denke ich
charakteristisch für Interrupts sind würde ich gerne noch geklärt wissen:
Beim Erhöhen von PortB mußte ich intervenieren, da der nicht einfach
ueberlief und wieder bei Null haengen blieb, sondern immer die oberen
beiden Bits "haengen" bleiben.
Die Sache mit dem Schalterprellen konnte ich nicht einfach dadurch beseitigen, dass ich den Interrupt in der entsprechenden ISR ausgeschaltet
habe. Abhilfe?
In jedem Falle vielen vielen herzlichen Dank an izaseba!Code:// Demoprogramm zur Verwendung von Interrupts am Beispiel der externen Interruptquellen (Int1 Int0) // Autor: Bachmayer // Review Status: NONE //Wissenswertes zu Interrupts auf AVRs: // Global Interrupt Enable I-Bit im GICR Register ist normalerweise LOW wenn Interupt ausgeführt wird // Interrupts haben zwar eine Priorität, können sich aber normalerweise nicht gegenseitig unterbrechen // Interrupt Schachtelung mittels manuellem I-Bit setzen möglich // Information im Statusregister SREG geht bei Interruptausführung verloren wenn man nicht sorgetraegt // Befehle: _SEI() = Set I Bit - Aktiviert Interruptfaehigkeit // _CLI()= Clear I Bit - Schaltet Interruptfaehigkeit aus // _SLEEP() = uC geht in den Sleep Modus und wartet auf naechsten Interrupt // // // Die Verfuegbaren Interrupt Signale entnommen aus der io.h wo diese den entsprechenden Interruptvektoren zugeordnet werden: // #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) // #define SIG_2WIRE_SERIAL _VECTOR(17) // #define SIG_SPM_READY _VECTOR(18) // // #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> #include <avr/delay.h> int8_t m; void main(void) { DDRB=0xff; DDRD=0x00; // Kompletter D Port wird hier als Input definiert. Beachte, auch als Output Interrupt möglich! //Konfiguration eines externen Interrupts (Alternativfunktion int0 int1 Pins) im MCUCR Register: // ISC11/ISC10 fuer INT1; ISC01/ISC00 fuer INT0; //ISC11 ISC10 Description //0 0 The low level of INT1 generates an interrupt request. //0 1 Any logical change on INT1 generates an interrupt request. //1 0 The falling edge of INT1 generates an interrupt request. //1 1 The rising edge of INT1 generates an interrupt request. cli(); // i Bit in Statusregister gelöscht=> Interrupts deaktiviert MCUCR=(1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00); // Definition der Ereignisse fuer die entsprechenden Interrupts GICR=(1<<INT1)|(1<<INT0); // Nun sind die Interrupts scharf sei(); // i Bit in Statusregister gesetzt=> Interrupts koennen ab jetzt auftreten for(;;) { } } SIGNAL(SIG_INTERRUPT0) // ISR fuer die erste Service Routine: Zaehlt einfach den PortB hoch { GICR=0x00; //A: Versuch Schalterprellen zu beseitigen, maessiger Erfolg ... m=PINB; if (m>0x2e) {m=0;} PORTB=m+1; _delay_ms(250); // warten... um garantiert nicht schneller zu sein wie Schalterprellen... _delay_ms(250); // GICR=(1<<INT1)|(1<<INT0); // A: Externe Interrupts wieder aktiviert } SIGNAL(SIG_INTERRUPT1) // ISR fuer zweiten externen Interrupt: Setzt einfach charakteristisches Muster... { PORTB=0x0a; }
uC
Ich bin ja ein verspieltes Kind, wenn ich schon dabei bin neue Sachen auszuprobieren, wollt ich den AVR mal in den Schlaf wiegen, aber das will nicht funzen. Kann mir jemand das oben gepostete Programm so ergänzen,
dass der AVR immer wieder aus dem Schlaf gerissen wird, ein paar Sekunden
wach bleibt (_delay_ms()... o.ä.) damit man das Ergebniss des Interrupts bewundern kann und dann wieder wegschläft...?
Ok, nun eine neue Spielerei, ich wollte den 16Bit Timer als PWM Quelle
nutzen, und das PWM Verhältniss sinusförmig variieren.
Das ganze sollte nicht in ner Schleife passieren, sondern mittels eines
Interrupts, der auf das Output Compare Match reagiert, und den OCR1A Eintrag des Timers Sinusförmig mit der Zeit ändert.
Schön so weit, nur wenn ich sei(); mache, also die Interrupts aktiviere
ist plötzlich Tote Hose!!!! Vertraegt sich der PWM nicht mit Interrupts???
Lesezeichen