PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Einfache ISR mittels GCC (Anfänger)



Propeller Clock
10.07.2011, 17:42
Liebe Roboternetz-Gemeinde,

früher hatte ich bereits kurz mit den Mikrocontrollern zu tun gehabt (kleines Schulprojekt) und
bin nun aber auch auf den Geschmack gekommen. Obwohl ich ehrlich gesagt noch nicht so die Ahnung in Sachen
Elektrotechnik habe, bin ich bereit mich dahinter zu klemmen und voll durchzustarten.

Ein paar Dinge habe ich auch schon bereits geschafft:

Mein Steckboard ist seit ein paar Stunden folgendermaßen bestückt:

-> Stromversorgung via Festspannungsregler
-> ATmega644P + Ext. 20Mhz Quarz
-> 4x 5V LED`s an Ports (PORTB 0 - 3)

Ansteuerung der LED`s funktioniert wunderbar über folgenden Code...



#include <avr/io.h>
//definiert den CPU Takt
#define F_CPU 20000000UL
#include <util/delay.h>
#include <inttypes.h>
#include <avr/interrupt.h>

int main()
{
DDRB |= (1<<0) | (1<<1) | (1<<2) | (1<<3); // LED-Ausgänge Output schalten...

// Die LED`s werden der Reihe nach ein bzw. ausgeschaltet...
while(1){

PORTB |= (1<<0);
_delay_ms(500);
PORTB &= (0<<0);
PORTB |= (1<<1);

_delay_ms(500);
PORTB &= (0<<1);
PORTB |= (1<<2);

_delay_ms(500);
PORTB &= (0<<2);
}
}



Nun würde ich es ganz gerne schaffen, das ich auf Ereignisse wie bspw. das Drücken von Tastern (PortA0 [INT0]) reagieren kann.
Diese Routinen sollen natürlich nicht im Hauptprogramm Main als Schleife verewigt werden, sondern "nebenbei" herlaufen.
Also über eine ISR wenn ich das richtig verstanden habe. Leider habe ich noch nicht wirklich den Bogen raus, wie das ganze funktionieren soll.

Finde leider keine simplen Beispiele dafür. Und wenn dann nur für Assembler oder Bascom.

Kann mir wer einen Tipp geben?


Vielen Dank schonmal im vorraus


Propeller Clock

021aet04
10.07.2011, 17:55
Willkommen im Forum,
hier habe ich ein Beispiel von Mikrocontroller.net. Es verwendet zwar einen Timerinterrupt, aber es ist einfach zu verstehen, da es nicht sehr groß ist. Es werden nur die für das Programm nötigen Befehle verwendet. Wie und welche Befehle man verwenden muss steht im Datenblatt des µC. Hier der Link http://www.mikrocontroller.net/articles/Interrupt#Steuersignale_zwischen_ISR_und_Hauptprog ramm .

PS: Es wird hier nicht gerne gesehen, wenn man nicht sucht. Es ist Eigeninitiative nötig.

MfG Hannes

Propeller Clock
10.07.2011, 18:08
Vielen Dank 021aet04!

Auf Mikrocontroller.net, Roboternetz.de etc. war ich bereits unterwegs und habe mich versucht schlau zu lesen.
Beim Datenblatt des Atmega644P sind natürlich die einzelnen Register gelistet und es wird auch kurz angesprochen welche Abhängigkeiten diese haben etc.
Ich suche allerdings wirklich nach einem Programmierbeispiel für GCC welches mir veranschaulicht wie in die nötige Funktion gesprungen wird und welchen Aufbau diesen haben müssen.

Selbstverständlich will ich nicht das mir einer eine Lösung schreibt und ich daraufhin "Copy-Paste" betreiben kann. Ich wills ja verstehen.
Die Timerinterrupt-Lösung wie sie oben verlinkt ist prüft ja auch nur in einer Endlosschleife ob "Flag" gesetzt wurde. Eben genau das möchte ich ja vermeiden.
Wenn mein Programm später größer wird, soll es nicht erst 400ms dauern bis darauf reagiert werden kann. Ich habe allerdings auch gelesen das man die Schritte nicht selbst
in der ISR abarbeiten soll.


MfG Propeller Clock

BurningWave
10.07.2011, 18:21
Tasten werden normalerweise immer in einer Schleife innerhalb des Hauptprogramms (oder einer Unterfunktion) abgefragt. Externe Interrupts sollte man nur verwenden, wenn man sie wirklich braucht (z.B. Not-Aus-Funktion etc.), da man nur 2 zur Verfügung hat.
Hier ein paar Links:
http://diyundso.de/?page=4&ref=1
http://www.mikrocontroller.net/articles/Interrupt
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmieren_mit_Interrupts

021aet04
10.07.2011, 18:41
Das Programm ist ein Beispiel. Du musst die einzelnen Register ändern. Diese findest du im DB (Datenblatt). Die verwendeten Register im Beispiel ist "TCCR2" und "TIMSK". Du musst das Programm jetzt ändern.
Ich verwende dieses DB http://www.atmel.com/dyn/resources/prod_documents/doc2593.pdf
Ab Seite 60 fängt das Kapitel Externe Interrupts an.
Du musst jetzt die richtigen Register heraussuchen. Z.B. das EICRA, EIMSK
Wenn du jetzt den INT 0 mit steigender Flanke verwenden willst musst du das in den entsprechenden Registern einstellen.

.........
EICRA |= (1<<ISC00) | (1<<ISC01);
EIMSK |= (1<<INT0);
.....
sei();
......
while(1)
{
.....
}

Es gibt aber auch noch andere Interrupts. Z.B. die PCINT (Pin Change Interrupts). Diese reagieren auf einen Flankenwechel (egal ob rising oder falling edge).

Die ISR musst du natürlich auch ändern. Das wirst du aber schon schaffen. Man sollte zwar nicht zuviele Zeilen in der ISR haben, aber wenn es nur z.B. 5 Zeilen sind mache ich die Auswertung direkt in der ISR. Wenn man mehr machen muss sollte man es so machen, wie im nächsten Beispiel im Link, den ich vorher gepostet habe (UART Beispiel).

PS: Wenn man mit Interrupt arbeitet muss man die Interrupts freigeben. Eine generelle Freigabe wir mit "sei();" gemacht, man kann es aber auch sperren, das ist wichtig wenn etwas wichtiges auf keinen Fall unterbrochen werden darf. Dieser Befehl lautet "cli();". Etwas das auch wichtig ist ist bei der Deklaration von Variablen das "volatile". Für was das ist gibt es Google.

MfG Hannes

Propeller Clock
10.07.2011, 20:45
Okay ich hab nun eigentlich versucht das ganze über PCINT laufen zu lassen, allerdings funktioniert es noch nicht so wie ich`s mir erhofft hatte.



#define F_CPU 20000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile uint8_t flag;

int main(){

// #### IO`s etc. konfigurieren....
DDRB |= (1<<PB0); // LED Output schalten...
flag = 0;


EICRA |= (1<<ISC00) | (0<<ISC01); // Modus: "Any edge of INT0 generates asynchronously an interrupt request"
EIMSK |= (1<<INT0);


PCMSK0 |= (1<<PCINT7); // PA7 als Interrupt-Pin....
PCICR |= (1<<PCIE0); // Das dazugehörige Register...

// ISR`s freigeben...
sei();

while(1){
// Endlosschleife...
if(flag==1){
_delay_ms(3000);
PORTB &= (0<<PB0); // LED AUS...
flag=0;
}
}

}


ISR( PCINT0_vect ){
flag = 1;
PORTB |= (1<<PB0); // LED AN...
}


Die LED fängt direkt nach gefühlten 600ms an zu leuchten und schaltet nach 3 Sekunden ab.
Das mit dem abschalten stimmt ja soweit, aber wieso geht diese einfach von alleine an?

Hab bis auf den ISP und der LED (PB0) noch nichts weiter angeschlossen.

Hat wer nen Tipp? Oder sagt Ihr euch, ich sollte morgen lieber nochmal komplett neu ansetzen ;)

MfG

Propeller Clock

Besserwessi
10.07.2011, 21:20
Offene Eingänge sind sehr empfindlich und fangen gerade mit den Steckbrett schnell mal Störungen ein. Das mindeste ist es die internen Pullup Widerstände einzuschalten (DDRA auf 0, also Eingänge und PORTA auf 1) . Auch damit kann es noch zu gelegentlichen Störungen kommen.

Propeller Clock
10.07.2011, 22:04
Gehe ich doch noch heute Abend mit einem kleinem Erfolgserlebnis zu Bett *freu* =)

Wenn auch einwenig kompliziert geschrieben der Code, aber er funktioniert. :)

Der nächste Schritt wird sein, meinen Infrarot-Empfänger auszuwerten. Aber damit mache ich morgen weiter.

Vielen Dank an euch drei !!!


Ps.: Auf den Teil mit der Störungsempfindlichkeit ... darauf wäre ich ja nie gekommen. ;)



#define F_CPU 20000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile uint8_t flag;

int main(){

// #### IO`s etc. konfigurieren....
DDRB |= (1<<PB0); // LED Output schalten...


DDRA &= ~( (1<<PA0) | (1<<PA1) | (1<<PA2) | (1<<PA3) | (1<<PA4) | (1<<PA5) | (1<<PA6) | (1<<PA7) );
PORTA |= (1<<PA0) | (1<<PA1) | (1<<PA2) | (1<<PA3) | (1<<PA4) | (1<<PA5) | (1<<PA6) | (1<<PA7);

flag = 0;


EICRA |= (1<<ISC00) | (0<<ISC01); // Modus: "Any edge of INT0 generates asynchronously an interrupt request"
EIMSK |= (1<<INT0);


PCMSK0 |= (1<<PCINT7); // PA7 als Interrupt-Pin....
PCICR |= (1<<PCIE0);

// ISR`s freigeben...
sei();

while(1){
// Endlosschleife...
if(flag==1){
_delay_ms(3000);
PORTB &= (0<<PB0); // LED AUS...
flag=0;
}
}

}


ISR( PCINT0_vect ){
flag = 1;
PORTB |= (1<<PB0); // LED AN...
}

021aet04
10.07.2011, 22:35
Sehr schön, wenn es funktioniert. Wenn du mehrere Pins gleichzeitig konfigurieren willst (wie bei dir DDRA bzw PORTA) kannst du die kürzere Schreibweise mit Hexadezimalzahlen verwenden.


Wie man das anwendet findet man im Internet.

MfG Hannes