Forum: Compiler & IDEs Frage zu Grundlagen der Interrupt Programmierung mit GCC und AT91SAM7


von Christian S. (lemartes)


Lesenswert?

Ich programmiere grade ein Eval-Board von Atmel mit dem SAM7X256. Als 
Entwicklungsumgebung nehme ich Yagarto und ich nutze als Basis den 
Beispielcode von Yagarto für das Board.

Ausgänge schalten und Eingänge abfragen klappt, auch die 
Interruptregister auslesen klappt, aber eben nicht das direkte ausführen 
einer Funktion, wenn der Interrupt auslöst. (Bisher frage ich quasi 
ständig ab, ob eine Änderung in dem Interruptregister steht, das ist im 
späteren Betrieb aber nicht mehr möglich)

Ich habe mir das Datenblatt durchgelesen und soweit auch verstanden. 
Also Interruptquelle muss generell aktiviert werden, das ist soweit 
passiert.
Nun müssen auch die Adressen in Form einer Vector Tabelle angegeben 
werden. Mir ist klar warum, nur wie das ganze im Quelltext umgesetzt 
wird (also Syntax etc) fehlt mir.
Habe bisher keine Erfahrung mit Microcontrollern, mir würde schon ein 
Bispielprogramm helfen.

Bisher habe ich schon von ramfunc und attribute interrupt gelesen, aber 
wo und wie das ganze dann geschrieben werden muss, verstehe ich nicht 
nicht ganz. Vielleicht kann mir hier ja jemand helfen.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Christian S. schrieb:
>
> Nun müssen auch die Adressen in Form einer Vector Tabelle angegeben
> werden.

Der Controller verfügt über einen Interrupt Controller (AIC). Es düfte 
nützlich sein, sich zuerst mit dem Interrupt-Konzept der ARM7TDMI-Kerns 
zu informieren (z.B. im Infocenter von arm.com) und dann im Manual des 
AT91SAM7 zu lesen, wie der AIC damit verbunden ist. Der "Vector-Tabelle" 
bezieht sich erstmal auf den Kern (Reset, IRQ, FIQ, SWI etc.). Der AIC 
bietet die Möglichkeit, Peripheral Interrups direkt anzuspringen, die 
Adresse der Serviceroutine wird bei Setup in AIC Regsiter geschrieben.

> Mir ist klar warum, nur wie das ganze im Quelltext umgesetzt
> wird (also Syntax etc) fehlt mir.

Es gibt im Grunde drei Möglichkeiten:

(a) Die ISR wird mit einem speziellen Attribut für Interrupts versehen 
(u.a. IRQ). Damit fügt der Compiler Maschinencode ein, der für ISRs 
erforderlich ist (z.B. Rücksprungadresse anpassen). Ist analog zu den 
__IRQ Erweiterungen der Compiler von ARM und IAR. Dieser Ansatz hat bei 
älteren GNU-Compilerversionen nur unter bestimmten Bedingungen 
funktioniert. Mag sich inzwischen gebessert haben.

(b) Die ISR wird mit einem naked Attribut versehen, die den Compiler 
davon abhält die für normale Funktionen erforderlichen Codes einzufügen. 
Über Inline-Assembler werden dann "von Hand" die Instruktionen für eine 
ISR eingefügt. Quasi ein Workaround zu den in (a) erläuterten Problemen. 
Spröde. Nach Möglichkeit nicht so machen.

(c) Eine zentrale Verteilerfunktion wird bei jedem Core-IRQ aufgerufen, 
bereitet den Spung in einen Verarbeitungsroutine vor, fragt den AIC nach 
der Routinenadresse, springt zur Routine und räumt hinterher auf. Die 
Behandlungsroutine selbst kann dann normaler C-Code sein (kein Attribut, 
kein Inline-Assembler). Da es eine solche "Verteilerfunktion" fertig im 
Beispielcode von Atmel gibt (atmel.com, AT91SAM7S/X-EK Seite) und da 
diese Funktion noch ein paar Extras bietet, kann man sie für den Anfang 
verwenden, bis man die erste Timer-Interrupt-Anwendung in Betrieb hat. 
Nachteil dieses Ansatzes: ein paar Prozessorzyklen Überhang.

> Habe bisher keine Erfahrung mit Microcontrollern, mir würde schon ein
> Bispielprogramm helfen.

Welche Suchmaschine wirft bei der Suche nach AT91SAM7 example code 
nichts aus? Die Controller sind zwar nicht mehr brandaktuell aber 
Beispielcode gibt es weiterhin an vielen Stellen.

Ansonsten mit dem Code von Atmel anfangen (den gibt es für verschieden 
Toolchains inkl. GNU). Damit sind die Chancen größer, Unterstützung zu 
finden. Nicht vom Umfang erschrecken lassen, es sind auch ein paar 
einfache Beispiele dabei. Wer es genau wissen will, schaut durch den 
Startup-Code (ist ARM-Assembler) und versucht das "warum" hinter jeder 
Anweisung zu verstehen - zumindest grob.

> Bisher habe ich schon von ramfunc und attribute interrupt gelesen, aber
> wo und wie das ganze dann geschrieben werden muss, verstehe ich nicht
> nicht ganz. Vielleicht kann mir hier ja jemand helfen.

"ramfunc" hat mit Interruptbehandlung direkt nichts zu tun, sondern 
dient zur Laufzeitoptimierung. Ist für den nächsten Lernabschnitt.

Lesenswert besonders weil schon für AT91SAM7 (ob S oder X spielt bei den 
Grundlagen erstmal keine Rolle): 
http://www.state-machine.com/resources/articles.php -> Building 
bare-metal ARM Systems with GNU.

von Christian S. (lemartes)


Lesenswert?

Danke erstmal, der Artikel der EE-Times sieht ganz interessant aus, das 
werde ich morgen mal genauer lesen.
Ich bevorzuge eigentlich Version a, die notwendige Syntax habe ich schon 
herausgefunden.

Wie bekomme ich denn raus, wo die Funktionen abgespeichert werden? 
(Linkerscript nehme ich an, fiel mir grad erst ein und ich komme erst 
morgen wieder an meine Files)
In den Registern des AIC gibt es ja auch die Abteilung, wo eine passende 
Vector Adresse eingetragen werden muss, die müsste ja entsprechend 
gewählt werden.

Im Interrupt Pending Register wird der Interrupt schon angezeigt (von 
PIO_A). Wird dann direkt die abgespeicherte Vector Adresse angesprungen 
oder muss noch etwas beachtet werden?

Beispielcode habe ich bisher für Variante B gefunden, inline Assembler 
fällt bei mir aber flach bin eher VBA und Delphi gewohnt.

Sind die AIC Controller so identisch? Beim LPC2468 sieht ja sehr viel 
anders aus bei den Periphals. (den hatte ich mir erst angesehen, aber 
wegen falscher Linker und Lowlevel Files nicht in Betrieb nehmen 
können...)

von Christian S. (lemartes)


Lesenswert?

Habe mich nun noch ein wenig damit befasst.
Eine Verteilerfunktion ist eigentlich das einfachste, aber ein 
Beispielprogramm von Atmel habe ich damit nicht gefunden bisher. Die 
anderen Beispielprogramme von Atmel habe ich mir auch angesehen, habe 
aber nichts zum Thema Interrupt gefunden (zumindest nichts, was ich 
verstanden habe, muss ich ehrlich sagen).

Ich bekomme ja problemlos die Interruptquellen in das Interrupt Pending 
Register geschrieben, eine Adresse habe ich auch eingetragen als 
Vectoradresse. (Habe ich einfach durch das ausführen der Funktion 
herausgefunden, da stand dann eine Adresse bei, aber das sollte doch 
auch irgendwie einfacher gehen oder?)

Beim Anzeigen lassen der aktuellen Interruptfunktion bekomme ich aber 
nur 0 zurückgeliefert.

Kann ich irgendwie prüfen, ob überhaupt die Interrupts im Prozessor 
aktiviert sind? Ich gehe momentan schon davon aus, dass es daran auch 
schon hapert.

Im Startupfile steht:
1
IRQHandler:
2
   b IRQHandler
ruft das automatisch ein Funktion IRQHandler auf, sofern vorhanden? Oder 
was macht das? (also wenn ein Interrupt an den Prozessor gesendet wird 
und die aktiviert sind)

Im Linkerfile ist auch eine IRQ-Stacksize angegeben, was macht die? Ich 
habe dazu leider nicht wirklich Informationen gefunden.

Und was mache ich dann, wenn ich Timer-Interrupts habe? Ich muss alle 10 
oder 20ms (muss ich noch festlegen) CAN-Botschaften senden... Wenn ich 
dich richtig verstehe funktioniert das dann nicht richtig oder ist das 
dann nur bei extrem kurzen Timerzeiten, wo es Probleme gibt?

von Christian S. (lemartes)


Lesenswert?

Nun wird's kurios.

Ich habe nur was am CAN-Controller geändert und auf einmal lösen die 
Interrupts aus, werden aber leider immer wieder durchlaufen (obwohl ich 
den Befehl zum Beenden des Interrupts gesetzt habe).
Bzw. lösen die Interrupts schon wieder nicht mehr aus, habe alle 
Befehle, die ich hinzugefügt oder weggelassen habe überprüft, egal was 
ich ändere, mittlerweile bleiben die Befehle einfach nur im Pending 
Register.

Komischerweise wird auch manchmal das SVR Register gefüllt (also das, wo 
beim entsprechenden Interrupt hingesprungen werden soll im AIC), 
allerding nicht immer und immer nur beim 1. Mal auslesen des Interrupts. 
Danach wird das Register wieder geleert und nicht wieder beschrieben und 
es wird auch nicht an die Adresse gesprungen, sondern einfach 
ignoriert...

Ich bin mit meinem Latein ein wenig am Ende...
Habe auch eine Funktion geschrieben, die im Startup File genannt wird 
(den IRQ_Handler), aber da ist er eben auch nur zum Anfang 
hingesprungen, wo er tatsächlich ausgelöst hat und nicht wieder rauskam. 
Mittlerweile leider nicht mehr.
Aber was kann so ein Verhalten hervorrufen?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.