Forum: Mikrocontroller und Digitale Elektronik ARM7, GCC und Interrupts


von Gabriel W. (gagosoft)


Lesenswert?

Hallo Leute!

Jetzt drehe ich mich schön langsam im Kreis....
Ich versuche die verschiedensten Beispiele zum Thema ARM7 und Vectored 
Interrupts aus, doch entweder mault mich mein Compiler an oder der 
Controller bleibt hängen. Oft ist bei den Beispielen kein Makefile 
dabei, mir unbekannt für welchen Compiler das Codestück geschrieben ist 
oder kein startup-code dabei.
Wie zeichnet gcc die Interrupt-Funktionen aus?

Die Fehlermeldung, die ich immer wieder bekomme:
1
error: interrupt Service Routines cannot be coded in Thumb mode

Wobei die Compiler-Zeile eigentlich NICHT THUMP compilieren sollte:
1
Compiling C (ARM-only): Time.c
2
arm-elf-gcc -c -mcpu=arm7tdmi -mthumb-interwork -I. -gdwarf-2 -DROM_RUN  -Os -Wall -Wcast-align -Wcast-qual -Wimplicit  -Wpointer-arith -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wunused -Wa,-adhlns=Time.lst   -MD -MP -MF .dep/Time.o.d -Wstrict-prototypes -Wmissing-declarations -Wmissing-prototypes -Wnested-externs  -std=gnu99 Time.c -o Time.o

Wer weis hier Rat oder ein feines, kleines, komplettes Beispiel eines 
NXP ARM7 Controllers mit Makefile für arm-elf-gcc.

mit der Bitte um Hilfe
Gabriel

von Lothar (Gast)


Lesenswert?

Das hat nichts mit dem gcc zu tun. Der ARM7 hat eine Vektortabelle bei 
Adresse 0 in der Sprünge zu den Handler-Funktionen stehen. Der 
Interrupt-Handler ist so eine Funktion. Da dieser nicht im User-Mode 
sondern im Protected-Mode läuft, muss dieser in Assembler geschrieben 
werden. Der Interrupt-Handler prüft die Interrupt-Nummer, z.B. Timer0, 
und springt dann zur Timer0-Interrupt-Funktion. Die kann dann in C 
programmiert werden.

Für ein Beispiel würde ich einen kommerziellen Compiler als Demo 
installieren, Keil oder IAR, und da den Code raus kopieren. Beim z.B. 
ARM7TDMI wären das startup.s, irq.s, irq.c

Beim Nachfolger CortexM3 ist das geändert worden, hier gibt es bei 
Adresse 0 eine Adressentabelle für alle Interrupt-Nummern. Hier kann 
dann z.B. die Timer0-Interrupt-Funktion in C programmiert werden.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

So schwer ist das nicht, wenn ich nachher mal bissel zeit habe schnipsel 
ich da mal etwas Code von hier zusammen.

Übrigens ARM hat keine Vektortabelle, sondern da müssen Befehle in die 
IRQ Tabelle.

Welchen IC hasten genau?

@über mir:
aber natürlich kann man alle Interruptbehandler in C schreiben, es 
benötigt nur einen kleinen ASM Teil drumrum zum Register retten ;)

von Siegfried W. (swausd)


Lesenswert?

Hallo Gabriel,

vor dem gleichen Problem stand ich vor einigen Wochen auch, als ich 
versucht habe, die Hintergründe zur Lern-Betty auf der Basis des LPC2220 
zu verstehen. Siehe hier:

Beitrag "die Betty-Fernbedienung von Pollin als ARM-Eval Board"

Dieses Projekt ist eine Interessante Basis für mich gewesen. Mir fehlte 
allerdings die gewünschte Kontrolle und ohne eigene Beschäftigung ist 
der Lerneffekt sehr gering.

Mit diesem Projekt und jeder Menge Zeit über den Jahreswechsel, dieser 
Seite

http://www.freddiechopin.info/en/download/category/6-examples

und natürlich mit der NXP Doku zur CPU habe ich mittlerweile für die 
Betty TV Umgebung auch FeeRTOS laufen. Zurzeit ist die Doku - speziell 
die Copyright Zuordnung - nicht für eine allgemeine Weitergabe geeignet.

Als Compiler verwende ich Yargarto bzw. aktuell auch

https://launchpad.net/gcc-arm-embedded/+milestone/4.7-2012-q4-major

weil diese Toolchain auch für MacOS X angeboten wird. Ich verwende 
keinen Thumb-Code. Meine Übersetzung erfolgt mit diesen Parametern

/Users/xyz/arm-toolchain/gcc-arm-none-eabi-4_7-2012q4/bin/arm-none-eabi- 
gcc  -c -mcpu=arm7tdmi-s  -O3 -ffunction-sections -fdata-sections -Wall 
-Wstrict-prototypes -Wextra -std=gnu99 -g -ggdb3 -fverbose-asm 
-Wa,-ahlms=out/main.lst  -MD -MP -MF out/main.d -I.  main.c -o 
out/main.o

Ich bin kein großer gcc Experte, vermute aber, dass Dein Problem in 
-mthumb-interwork begründet ist.

Vielleicht hilft Dir mein Beitrag...

Gruß

swausd

von Gabriel W. (gagosoft)


Lesenswert?

Ich hab das LPC2478-STK von Olimex vor mir und bekomme zwar ein Bild auf 
dem Display aber keinen Interrupt.

Ich fürchte schon, das das so langsam mit meinem GCC zu tun hat.
Hab gerade ein Paar Beispiele, die zu den Olimex-ARM7-Prints angeboten 
werden heruntergeladen. Da ist auch ein Makefile dabei. In der main.c 
stehen die Interrupt-Funktionen drinnen:
1
void IRQ_Routine (void)   __attribute__ ((interrupt("IRQ")));
2
void FIQ_Routine (void)   __attribute__ ((interrupt("FIQ")));
3
void SWI_Routine (void)   __attribute__ ((interrupt("SWI")));
4
void UNDEF_Routine (void) __attribute__ ((interrupt("UNDEF")));
5
6
...
7
8
void IRQ_Routine (void) {   // <--- hier ist Zeile 138
9
  while (1) ;  
10
}
11
12
void FIQ_Routine (void)  {
13
  while (1) ;  
14
}
15
16
17
void SWI_Routine (void)  {
18
  while (1) ;  
19
}
20
21
22
void UNDEF_Routine (void) {
23
  while (1) ;  
24
}

Mein Compiler mault (mit und ohne -mthumb-interwork):
1
arm-elf-gcc -I./ -c -fno-common -O0 -g main.c
2
main.c: In function 'IRQ_Routine':
3
main.c:138:1: error: interrupt Service Routines cannot be coded in Thumb mode
4
make: *** [main.o] Fehler


Du hast vollkommen recht, der CortexM3 ist da viel gutmütiger und 
geradliniger zu programmieren! In Zukunft werde ich die ARM7 altes Eisen 
sein lassen und mit den Cortex cores arbeiten.

Den Code für den Cortex übersetzt der Compiler brav und gut.
Ich hab mir nochmal die ./configure Einstellungen angesehen und nichts 
verdächtiges dabei herausgefunden.
1
configure --target=arm-elf --prefix=/usr/local/arm-elf --disable-nls --disable-shared --disable-threads --with-gnu-ld --with-gnu-as --disable-multilib --disable-libssp --disable-libmudflap --disable-libgomp --with-dwarf2 --with-newlib -v --disable-werror --with-cpu=cortex-m3 --with-tune=cortex-m3 --with-mode=thumb --enable-target-optspace --with-float=soft --enable-languages=c,c++ --with-cpu=arm7tdmi --with-tune=arm7tdmi --enable-interwork
Damit sollte eigentlich alles im Compiler drinnen sein...

Gruß Gabriel

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Wir hamms ja aufm ARMv4 geproggt, wenne den Code trotzdem sehen willst, 
dann sag bescheid.

Da ist allerdings alles zu Fuß gemacht, also Vektortabelle in ASM 
anlegen, Stacks initialisieren, Register retten.
Nur der IRQ selber ist dann C.

von W.S. (Gast)


Lesenswert?

Gabriel Wegscheider schrieb:
> Jetzt drehe ich mich schön langsam im Kreis....

Schön ruhig bleiben.

Das Interrupt-Geschehen beim ARM7TDMI geht etwa so:

- das Init-Programm lädt die Adresse der gewünschten Interruptroutine in 
den Interruptcontroller. Das gilt für vektorisierte Interrupts. Der FIQ 
läuft anders, die Softwareinterrupts auch und alle freigegebenen 
Interrupts, die von dir keinen Vektor abbekommen haben, landen in einem 
Sammelinterrupt.

- erfolgt ein Interrupt, dann schaltet die Hardware auf einen anderen 
Stack um, geht in den ARM Modus und startet ein winziges Programm, das 
zumeist nur eines macht:
  LDR     PC,[PC, #-0x0FF0]      ; 18h  IRQ, HandlerAddr-->PC
Damit wird die im Interrupt-Controller gespeicherte Anfangsadresse der 
Serviceroutine angesprungen.

Merke: Hier sind wir im ARM-Modus und deine Int-Serviceroutine MUSS 
ebenfalls im Arm-Modus compiliert sein. Blöderweise versteht der GCC die 
dafür nötigen Pragmas nicht, weswegen du zwangsweise die 
Interruptroutinen in eine separat zu übersetzende Quelle auslagern mußt. 
Anders geht das beim GCC nicht. Mit dem Keil hat man dieses Problem 
nicht, aber die diversen Diskussionen darüber und über andere Dinge 
(SVC, Thumb mode usw) hatte wir schon mal. SVC geht nur per Wrapper, für 
Thumb in Assembler muß man .thumb und zusätzlich .thumb_func schreiben 
und so weiter. Wahrscheinlich bin ich bei der Lernbetty noch längst 
nicht über alle GCC-Fallstricke gestolpert. Lies dich dort mal ein, da 
kannst du sehen, wie diverse Sachen so im Detail funktionieren.

Gabriel Wegscheider schrieb:
> oder ein feines, kleines, komplettes Beispiel eines
> NXP ARM7 Controllers mit Makefile für arm-elf-gcc.

Tja, die Lernbetty ist so ein kleines, feines Beispiel (eben genau dazu 
gedacht) - aber halte dich nicht so sehr an irgendwelchen Makefiles 
fest, sondern guck daß du ohne ein solches der Sache erstmal echt zu Fuß 
auf den Grund gehst. Wenn dir dann alles klar ist, kannst du immer noch 
mit Makefiles arbeiten.

W.S.

von Gabriel W. (gagosoft)


Lesenswert?

Wie der Interrupt theoretisch funktioniert ist mir soweit klar, doch wie 
sag ich's meinem Compiler.
Stimmt die Auszeichnung
1
 __attribute__ ((interrupt("IRQ")))
 für den Vectored interrupt beim GCC?
Müssen die Interrupts vor der Benutzung aktiviert werden oder sind die 
aktiv sobald die Handler-adressen und Prioritäten eingetragen sind?

Mein Compiler will aber offenbar nicht im ARM-Mode übersetzen....
Dieser ist selbst laut Beschreibung übersetzt und ich hab oben mal die 
config beschrieben.

Gruß
Gabriel

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Weis jetz nich was dein Compiler macht, abern nachm Reset ist bit7 im 
SPSR gesetzt (IRQs aus).

Kannste dir ja mal printen lassen.

von W.S. (Gast)


Lesenswert?

Gabriel Wegscheider schrieb:
> Mein Compiler will aber offenbar nicht im ARM-Mode übersetzen....

Nochmal: Schau dir die Lernbetty an, Rubrik Codesammlung.
Natürlich kann auch der GCC im Arm-Mode, du mußt es ihm bloß sagen. Ich 
hab wirklich nicht umsonst geschrieben, daß du besser für den Anfang 
ohne IDE, ohne Makefile und ohne sonstigen Krimskrams compilieren 
solltest. Wenn du dann weißt, wie es geht, hast du auch nen besseren 
Blick auf das, was sich da in deiner IDE, Makefile etc. so herumtummelt.

"Müssen die Interrupts vor der Benutzung aktiviert werden oder sind die
aktiv sobald die Handler-adressen und Prioritäten eingetragen sind?"

Da gehört mehr dazu:
- Adresse in den Slot deiner wahl eintragen (wg. Priorisierung),
- Quelle im Slot eintragen
- Zuordnung FIQ, INT, Sammelint im Slot eintragen
- Peripherie aufsetzen und dort die Interrupt-Erzeugung freischalten
Jaja, ist ne komplexe Sache.


Martin Wende schrieb:
> Weis jetz nich was dein Compiler macht, abern nachm Reset ist bit7 im
> SPSR gesetzt (IRQs aus).

Und? Was soll das?

Der Startupcode ist auch dazu da, die diversen Stacks und Modi 
aufzusetzen. Dazu gehört eben auch, daß er schlußendlich in den Usermode 
geht, wo alle Interrupts CPU-mäßig freigegeben sind. Das heißt aber noch 
lange nicht, daß der Interruptcontroller selbige ebenfalls freigegeben 
hat oder gar die Peripherie selbige erzeugt.

Es gibt je nach ARM-Version und Hersteller unterschiedliche 
Interrupt-Controller. Deswegen MUSS!!! man das Manual zum Chip in dieser 
Angelegenheit gründlich lesen. Glaub's mir.

W.S.

von Gabriel W. (gagosoft)


Lesenswert?

Hallo W.S.
Ich fürchte, der Code der Lernbetty hilft mir nicht weiter.
Mein Problem scheint in der Kombination von ARM7 + Interrupt + "meinem 
GCC" liegen.

W.S. schrieb:
>       Gabriel Wegscheider schrieb:
>> Mein Compiler will aber offenbar nicht im ARM-Mode übersetzen....
>
> Nochmal: Schau dir die Lernbetty an, Rubrik Codesammlung.

> Natürlich kann auch der GCC im Arm-Mode, du mußt es ihm bloß sagen. Ich
> hab wirklich nicht umsonst geschrieben, daß du besser für den Anfang
> ohne IDE, ohne Makefile und ohne sonstigen Krimskrams compilieren
> solltest. Wenn du dann weißt, wie es geht, hast du auch nen besseren
> Blick auf das, was sich da in deiner IDE, Makefile etc. so herumtummelt.

Ich verwende keine IDE. Meine Programme werden von der Konsole aus via 
make kompiliert. Mit Makefile bin ich gut genug vertraut und kann diese 
- solange sie nicht mit automake generiert wurden - auch lesen und 
schreiben.

W.S. schrieb:
> "Müssen die Interrupts vor der Benutzung aktiviert werden oder sind die
> aktiv sobald die Handler-adressen und Prioritäten eingetragen sind?"
>
> Da gehört mehr dazu:
> - Adresse in den Slot deiner wahl eintragen (wg. Priorisierung),
> - Quelle im Slot eintragen
> - Zuordnung FIQ, INT, Sammelint im Slot eintragen
> - Peripherie aufsetzen und dort die Interrupt-Erzeugung freischalten
> Jaja, ist ne komplexe Sache.
>
>
> Martin Wende schrieb:
>> Weis jetz nich was dein Compiler macht, abern nachm Reset ist bit7 im
>> SPSR gesetzt (IRQs aus).
>
> Und? Was soll das?
>
> Der Startupcode ist auch dazu da, die diversen Stacks und Modi
> aufzusetzen. Dazu gehört eben auch, daß er schlußendlich in den Usermode
> geht, wo alle Interrupts CPU-mäßig freigegeben sind. Das heißt aber noch
> lange nicht, daß der Interruptcontroller selbige ebenfalls freigegeben
> hat oder gar die Peripherie selbige erzeugt.
>
> Es gibt je nach ARM-Version und Hersteller unterschiedliche
> Interrupt-Controller. Deswegen MUSS!!! man das Manual zum Chip in dieser
> Angelegenheit gründlich lesen. Glaub's mir.
Das werd' ich mir ansehen, aber da liegt mein Hund nicht begraben
>
> W.S.

Gruß Gabriel

von Gabriel W. (gagosoft)


Lesenswert?

Wie schon gesagt, ich dreh mich im Kreis... und komme immer wieder bei 
den selben Fakten vorbei.

Jetzt zu einem Minimal-Beispiel das mein Dilemma zeigen soll:
Wie bekomme ich folgenden C-Code mit arm-elf-gcc in gültigen ARM-Code?
1
/* file: main.c */
2
/* Compile-error by Gabriel Wegscheider */
3
4
void IRQ_Routine (void)   __attribute__ ((interrupt("IRQ")));
5
void FIQ_Routine (void)   __attribute__ ((interrupt("FIQ")));
6
void SWI_Routine (void)   __attribute__ ((interrupt("SWI")));
7
void UNDEF_Routine (void) __attribute__ ((interrupt("UNDEF")));
8
9
#include "lpc210x.h"
10
11
int main(void) {
12
  for(;;)
13
    ;
14
}
15
16
void IRQ_Routine (void) {
17
        while (1) ;     
18
}
19
20
void FIQ_Routine (void)  {
21
        while (1) ;     
22
}
23
24
25
void SWI_Routine (void)  {
26
        while (1) ;     
27
}
28
29
30
void UNDEF_Routine (void) {
31
        while (1) ;     
32
}

Der Aufruf:
1
arm-elf-gcc  -mcpu=arm7tdmi -I./ -c -O0 -g main.c
fürhrt auch schon zu der Fehlermeldung:
1
main.c: In function 'IRQ_Routine':
2
main.c:17:1: error: interrupt Service Routines cannot be coded in Thumb mode

Gruß Gabriel

von (prx) A. K. (prx)


Lesenswert?

CodeSourcery frisst diesen Code (ohne das Include) anstandslos. Wär also 
die Frage, was für ein GCC das ist bzw. wie der so vergewaltigt wurde, 
dass er ohne Angabe von -mthumb dennoch Thumb Code erzeugt.

$ arm-none-eabi-gcc -mcpu=arm7tdmi -I./ -c -O0 -g irq7.c
OK

$ arm-none-eabi-gcc -mcpu=arm7tdmi -mthumb -I./ -c -O0 -g irq7.c
irq7.c: In function 'IRQ_Routine':
irq7.c:16:1: error: interrupt Service Routines cannot be coded in Thumb 
mode

gcc version 4.6.3 (Sourcery CodeBench Lite 2012.03-56)

von (prx) A. K. (prx)


Lesenswert?

Schalte mal explizit auf ARM:
  arm-elf-gcc  -mcpu=arm7tdmi -marm -I./ -c -O0 -g main.c

von (prx) A. K. (prx)


Lesenswert?

Wieso übrigens lpc210x.h mit LPC2478? Zwischen LPC210x und LPC2478 
liegen ein paar Jahre und insbesondere der VIC unterscheidet sich 
erheblich.

von Gabriel W. (gagosoft)


Lesenswert?

Danke A.K. !
Damit hast Du mir geholfen.

Ich glaub, ich hab den Fehler:
Beim Erstellen der Toolchain ist der Switch
1
 --with-mode=thumb
 in der configure des gcc. --> Default Thumb.  Wenn ich jetzt -marm als 
Argument mitgebe kompiliert er mir mal meinen Code anstandslos - das hab 
ich auch gerade bemerkt.

Wieso lpc21xx ?
Dieser Code stammt ursprünglich aus der Softwaresammlung der 
Olimex-Platinen, ist definitiv für den GCC geschrieben und zeigte den 
Compile-Fehler auch schon. In einer Eierlegenden-Wollmilchsau sah ich 
keine Chancen, dem Fehler auf den Grund zu gehen.

... jetzt kann ich mich wieder komplexeren Beispielen zuwenden

Gruß Gabriel

von (prx) A. K. (prx)


Lesenswert?

Es gibt bei den LPC2000 eigentlich nur einen Grund, den Thumb-Mode zu 
verwenden: wenn der Platz im ROM sonst nicht reicht. Anders als bei 
manchen anderen ARM7 µCs ist die Flash-Bandbreite der LPC2000 gross 
genug für den ARM-Mode.

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.