Hallo Zusammen,
bin gerade völlig mit den Nerven runter. Habe schon den ganzen Tag
versucht einen einfachen Timerinterrupt zu erleben.
Ich verwende Yagarto mit Eclipse usw. Der Controller ist ein LPC2148.
Habe den Timer konfiguriert:
TIMER0_MCR = 0x0003;
TIMER0_MR0 = 0x3E8;
TIMER0_TCR = 1;
Das VIC-Zeugs ebenfalls:
VICVectCntl0=0x20|4;
VICVectAddr0=(unsigned long )timer0;
VICIntEnable = 0x00000010;
Meine Interruptroutine schaut so aus:
void timer0(void) {
TIMER0_IR=1;
timercounter++;
VICVectAddr=0;
}
Der Prototyp so:
void timer0(void) _attribute_ ((interrupt("IRQ")));
Vielleicht hat von Euch ja jemand noch mehr aktive Zellen um den Fehler
zu finden.
Kann es sein, dass es so eine Art Global Interrupt Enable gibt? Wenn ja,
wie rufe ich es auf?
Viele Grüße
Jansus
Jansus wrote:
>...> Kann es sein, dass es so eine Art Global Interrupt Enable gibt?>...
Ja, gibt es. Am einfachsten beim Startup dafür sorgen, dass I_BIT und
F_BIT im CPSR des Modus, von dem aus main() aufgerufen wird, nicht
gesetzt sind. Wie im Detail, kann man erst schreiben, wenn Startup-Code
gezeigt wird. Irgendwo im Startup Assembler-Code werden die
Stack-Pointer der verschiedenen Modi gesetzt. In der letzten Zeile mit
der Form msr CPSR_c... schauen, ob ein |I_BIT|F_BIT am Ende steht (oder
|0x80|0x40). Wenn ja, löschen (denn I_BIT gesetzt heisst IRQ-Exceptions
aus).
Ansonsten, falls Code im System-Mode ausgeführt wird (üblich bei
"kleinen" ARM), kann man auch zur Laufzeit de-/aktivieren. In der Art:
1
#define IRQ_MASK 0x00000080
2
3
staticinlineunsignedlongget_cpsr(void)
4
{
5
unsignedlongretval;
6
asmvolatile("mrs %0, cpsr":"=r"(retval):/* no inputs */);
7
returnretval;
8
}
9
10
staticinlinevoidset_cpsr(unsignedlongval)
11
{
12
asmvolatile("msr cpsr, %0":/* no outputs */:"r"(val));
13
}
14
15
voidenableIRQ(void)
16
{
17
unsignedlongcpsr;
18
19
cpsr=get_cpsr();
20
set_cpsr(cpsr&~IRQ_MASK);// clear I_BIT in CPSR
21
}
Timer-init habe ich jetzt nicht genau nachvollzogen, ist immer etwas
mühsam mit Hex-Zahlen "Wüsten".
Ansonsten gibt es noch ein paar potentielle Fallen:
- _attribute_ ((interrupt("IRQ"))) funktioniert nicht in allen
"Lebenslagen" bein arm GCC
- IRQ-Exceptions-Vector muss PC aus der Vic-Addresse laden, falls kein
Assembler-Wrapper genutzt wird. Im Startup sollte bei LPC2000 (<2300) am
siebten Vektor sowas stehen:
ldr pc,[pc,#-0xFF0]
Martin Thomas
Hallo,
vielen Dank für die schnelle Antwort!
Habe inzwischen rausgefunden dass es wohl unterschiedliche Startupcodes
gibt. Mal mit Interrupt-Fähigkeit, mal ohne.
Da meiner recht kurz ausfällt, befürchte ich, dass ich den ohne
Interruptfunktionalität erwischt habe.
Leider bin ich ein absoluter Newbie was die ARMs angeht... Wie kann ich
die oben genannten Funktionen verwenden? Wenn ich sie mir einfach in den
Code reinkopiere, endet das im Absturz...
Ich fände es schon etwas praktischer, wenn ich Interrupts im laufenden
Programm de- bzw. aktivieren könnte.
Schonmal herzlichen Dank im Voraus!!!
Hier mal mein Startup-Code
/*
************************************************************************
***************************************
crt.s STARTUP ASSEMBLY CODE
-----------------------
Module includes the interrupt vectors and start-up code.
************************************************************************
*************************************** */
/* Stack Sizes */
.set UND_STACK_SIZE, 0x00000004 /* stack for "undefined instruction"
interrupts is 4 bytes */
.set ABT_STACK_SIZE, 0x00000004 /* stack for "abort" interrupts is 4
bytes */
.set FIQ_STACK_SIZE, 0x00000004 /* stack for "FIQ" interrupts is 4
bytes */
.set IRQ_STACK_SIZE, 0X00000004 /* stack for "IRQ" normal interrupts
is 4 bytes */
.set SVC_STACK_SIZE, 0x00000004 /* stack for "SVC" supervisor mode
is 4 bytes */
/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
(program status registers) */
.set MODE_USR, 0x10 /* Normal User Mode
*/
.set MODE_FIQ, 0x11 /* FIQ Processing Fast Interrupts
Mode */
.set MODE_IRQ, 0x12 /* IRQ Processing Standard
Interrupts Mode */
.set MODE_SVC, 0x13 /* Supervisor Processing Software
Interrupts Mode */
.set MODE_ABT, 0x17 /* Abort Processing memory Faults
Mode */
.set MODE_UND, 0x1B /* Undefined Processing Undefined
Instructions Mode */
.set MODE_SYS, 0x1F /* System Running Priviledged
Operating System Tasks Mode */
.set I_BIT, 0x80 /* when I bit is set, IRQ is
disabled (program status registers) */
.set F_BIT, 0x40 /* when F bit is set, FIQ is
disabled (program status registers) */
.text
.arm
.global Reset_Handler
.global _startup
.func _startup
_startup:
# Exception Vectors
_vectors: ldr PC, Reset_Addr
ldr PC, Undef_Addr
ldr PC, SWI_Addr
ldr PC, PAbt_Addr
ldr PC, DAbt_Addr
nop /* Reserved Vector (holds Philips ISP
checksum) */
ldr PC, [PC,#-0xFF0] /* see page 71 of "Insiders
Guide to the Philips ARM7-Based Microcontrollers" by Trevor Martin */
ldr PC, FIQ_Addr
Reset_Addr: .word Reset_Handler /* defined in this module below
*/
Undef_Addr: .word UNDEF_Routine /* defined in main.c */
SWI_Addr: .word SWI_Routine /* defined in main.c */
PAbt_Addr: .word UNDEF_Routine /* defined in main.c */
DAbt_Addr: .word UNDEF_Routine /* defined in main.c */
IRQ_Addr: .word IRQ_Routine /* defined in main.c */
FIQ_Addr: .word FIQ_Routine /* defined in main.c */
.word 0 /* rounds the vectors and ISR
addresses to 64 bytes total */
# Reset Handler
Reset_Handler:
/* Setup a stack for each mode - note that this only sets up a
usable stack
for User mode. Also each mode is setup with interrupts
initially disabled. */
ldr r0, =_stack_end
msr CPSR_c, #MODE_UND|I_BIT|F_BIT /* Undefined Instruction
Mode */
mov sp, r0
sub r0, r0, #UND_STACK_SIZE
msr CPSR_c, #MODE_ABT|I_BIT|F_BIT /* Abort Mode */
mov sp, r0
sub r0, r0, #ABT_STACK_SIZE
msr CPSR_c, #MODE_FIQ|I_BIT|F_BIT /* FIQ Mode */
mov sp, r0
sub r0, r0, #FIQ_STACK_SIZE
msr CPSR_c, #MODE_IRQ|I_BIT|F_BIT /* IRQ Mode */
mov sp, r0
sub r0, r0, #IRQ_STACK_SIZE
msr CPSR_c, #MODE_SVC|I_BIT|F_BIT /* Supervisor Mode */
mov sp, r0
sub r0, r0, #SVC_STACK_SIZE
msr CPSR_c, #MODE_SYS|I_BIT|F_BIT /* User Mode */
mov sp, r0
/* copy .data section (Copy from ROM to RAM) */
ldr R1, =_etext
ldr R2, =_data
ldr R3, =_edata
1: cmp R2, R3
ldrlo R0, [R1], #4
strlo R0, [R2], #4
blo 1b
/* Clear .bss section (Zero init) */
mov R0, #0
ldr R1, =_bss_start
ldr R2, =_bss_end
2: cmp R1, R2
strlo R0, [R1], #4
blo 2b
/* Enter the C code */
b main
.endfunc
.end
Ich blicke einfach nicht durch! Gibt's nicht vielleicht irgendwo eine
Art Tutorial in dem Startup-Codes erklärt werden?
Zurück zu dem, den ich benutze. Was muss ich tun, um den gewünschten
Mode einzuschalten? Und muss ich aus den 0x80 0x00 machen, um Interrupts
generell zu aktivieren?
Was ist eigentlich der Unterschied zwischen User und Systemmode?
???
So, habe in einem Tutorial von Jim Lynch die Files VIClowlevel.c und .h
gefunden.
Bin sie durchgegangen und kann sie glücklicherweise nachvollziehen.
Passt ja auch sehr zu der Antwort von Martin Thomas.
Habe die Dateien jetzt mal in mein Projekt eingebunden.
Compilieren und debuggen funktioniert.
Allerdings bleibt der µC in "asm_set_cpsr" hängen, bzw. führt die Zeile
"asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) );"
nicht aus.
Hat jemand eine Idee oder einen Tipp für mich?
Hi,
ich hatte auch mal das gleiche Problem, also dass er diesen Befehle
nicht kompiliert bzw. nicht ausführt ( Im Dissambly war er wegoptimiert
worden.. ) musste dann leider für das Setzen der Interruptflags 2
Assembler Funktionen schreiben. Das hat dann eigentlich problemlos (
dank freundlicher Hilfe hier im Forum ) funktioniert.
Thread:
Beitrag "ARM Nested Interrupts mit Keil µVision mit RealView?"
Danke für die Antwort!
Leider weiß ich mit Assembler nicht so viel anzufangen.
Aber es war schon die Datei Nested_IRQ.s gemeint, oder? Was muss ich mit
nested_irq_enable anstellen, damit es klappt? Ist das dann ein Ersatz
für "asm volatile (" msr cpsr, %0" : /* no outputs */ : "r" (val) );"
oder funktioniert meine obige Zeile dann erst?
Im gezeigten Startup-Script wird main() im System-Mode mit deaktivierten
IRQ- und FIQ-Exceptions ausgeführt:
1
...
2
msr CPSR_c, #MODE_SYS|I_BIT|F_BIT /* User Mode */
3
...
(der Kommentar irreführend)
Hatte bereits weiter oben geschrieben, dass das I_BIT nicht gesetzt sein
darf, um IRQ-Exceptions zu aktivieren. Also testweise ändern in:
1
...
2
msr CPSR_c, #MODE_SYS /* I_BIT cleared -> IRQ-Exception enabled, same for FIQ */
3
...
inline-assembler "Geschichte" erstmal weglassen, kann man später immer
noch nach schauen, wenn die eigentliche Interrupt-Verarbeitung
funktioniert.
Martin Thomas
Obwohl "alles abschmiert" erstmal ein Fortschritt, zumindest passiert
jetzt etwas und dies wahrscheinlich dann, wenn der Interrupt auslöst.
- VIC vor Aktivierung des Timer-Interrupts (TCR) eingestellt? Mglw.
läuft der Interrupt noch ins Leere (Timer Match-Interrupt "feuert" da
schon aktiviert aber VIC noch nicht entsprechend eingerichtet).
IRQ-Exceptions sind ja bereits vom Startup global freigegben. Diese
Reihenfolge mal ausprobieren:
(1) MCR, MRO (init timer)
(2) "VIC Zeugs" (configure VIC for Timer)
(3) TCR (enable timer)
- Default Interrupt Handler eingerichtet (Function auf die
VICDefVectAddr zeigt)? Wird dieser aufgerufen? Default-Handler sollte
man bei LPC2000 mit VIC190 (z. Zt. die <2300) immer einrichten, hilft
nicht nur bei "spurious interrupts" sondern auch bei der Suche nach
Fehlern in der VIC-Konfiguration.
Martin Thomas
Die Reihenfolge habe ich richtig. Erst VIC dann Timer. Aktiviere den
Timer auch erst kurz vor meiner Endlosschleife.
VICDefVectAddr habe ich überhaupt nicht verwendet. Ich dachte
"VICVectAddr0=(unsigned long )timer0;" würde alles managen?
Wie verwende ich VICDefVectAddr korrekt? Weise ich dem eine andere
Interrupt-Routine zu?
Übrigens vielen, vielen Dank für die Tipps!
Ich habe inzwischen einen anderen Startup-Code und ein anderes Makefile
verwendet. Habe meinen C-Code "integriert" und wer sagt's, Eclipse
kompiliert und debuggt.
Jedenfalls weiß ich jetzt was passiert. Wenn mal wieder alles
abschmiert, steht im Disassembler "0x400000e0 <IRQHandler>: b
0x400000e0 <IRQHandler>". Der Interrupt löst aus, aber ich lande nicht
in meiner Routine.
Nur dummerweise weiß ich hier nicht weiter...
Habe Makefile und Startup mal angehängt (wenn's klappt)
Okay, ich hab's! Muss im Startup statt IRQHandler halt meine Routine
reinschreiben.
Aber wie schaut's dann aus, wenn ich mehrere IRQ-Interrupts managen
möchte?
Da habe ich schon wieder was dazu gelernt.
Ich muss wohl einen IRQHandler selbst basteln und im Startup
integrieren.
Heißt das ich muss:
1. Register retten - aber welche?
2. Herausfinden wer oder was den Interrupt ausgelöst hat und dann in die
entsprechende Routine springen - aber wie?
3. Register wieder zurückschreiben und Programm weiterlaufen lassen
Stimmt das soweit?
Hat das vielleicht schonmal jemand gemacht?
Wenn du in C programmierst, was ich annehme, sollte eigentlich der
Compiler alle Register sichern. Was du per Hand retten musst ist das
Linkregister. Es sei denn dir reicht es, dass mehrere Interrupts
auftreten können, aber keine Priorisierung stattfindet. Für eine
Priorisierung brauchst du Nested Interrupts ( die 2 Assembler Funktionen
von vorher retten hier das Linkregister und "enablen" wieder alle
Interrupts ).
Das Linkregister musst du retten, da es für den IRQ Modus nur eines gibt
und bei einem weiterem Interrupt wäre nun deine Rücksprungadresse
verloren und du würdest im Nirgendwo landen, wenn du die ISR verlässt.
Damit diese Priorisierung möglich ist, musst du im Startup folgendes
ändern:
_vectors:
ldr pc, ResetAddr /* Reset */
ldr pc, UndefAddr /* Undefined instruction */
ldr pc, SWIAddr /* Software interrupt */
ldr pc, PAbortAddr /* Prefetch abort */
ldr pc, DAbortAddr /* Data abort */
ldr pc, ReservedAddr /* Reserved */
ldr pc, IRQAddr /* IRQ interrupt */
ldr pc, FIQAddr /* FIQ interrupt */
in
_vectors:
ldr pc, ResetAddr /* Reset */
ldr pc, UndefAddr /* Undefined instruction */
ldr pc, SWIAddr /* Software interrupt */
ldr pc, PAbortAddr /* Prefetch abort */
ldr pc, DAbortAddr /* Data abort */
LDR pc, [pc, #-0x0FF0] /* Vector from VicVectAddr */
ldr pc, FIQAddr /* FIQ interrupt */
ich hoffe, dass ich nichts übersehen habe.
Lasst die Sache mit nested Interrupts vorerst bleiben. Das ist was für
Fortgeschrittene. Bleib bei einfachen Interrupts ohne IRQ-Handler in
Assembler.
Der Befehl
ldr PC, [PC,#-0xFF0]
an der Stelle vom IRQ-Einsprung war schon also richtig. Da wird der
Vektor von VIC geholt und ein eigener Assembler IRQ-Handler ist
überflüssig.
Kleiner Nachteil: Ein sehr versionsresistenter GCC-Bug kann in
Einzelfällen zu einem fehlerhaften IRQ-Handler führen. Das Risiko
solltest du vorerst eingehen, und ggf. den vom Compiler erzeugten Code
überprüfen. IIRC wird manchmal bei der Handhabung der Return-Adresse
zweimal 4 abgezogen, einmal vorneweg, einmal hinten.
Vielen Dank!
Ich habe die Zeile ausgetauscht und unten wieder IRQHandler anstatt
meiner Routine eingesetzt.
Leider passiert jetzt wieder garnichts. Meine Endlosschleife wird
ausgeführt, aber von Interrupts keine Spur.
Muss ich nicht irgendwie die Zuordnung von dem Interrupt zu dem
IRQHandler herstellen?
Priorisierung möchte ich schon, aber ich bin erstmal schon unheimlich
glücklich wenn ich zwei Interrupts (beide IRQ, aber unterschiedliche
Quellen) hinbekomme.
Du musst den VIC programmieren. Sonst geht garnichts. Aber das tust du
schon (ob richtig habe jetzt ich nicht kontrolliert).
Zudem wird in den Startups üblicherweise und richtigerweise der
Interrupt noch nicht freigegeben. Das sollte nach der
Basisinitialisierung im Programm erfolgen. Dazu siehe die Funktionen von
Martin Thomas oben.
Ob der Interrupt überhaupt ausgelöst wird und bei VIC ankommt, kannst du
testhalber in der Schleife per Abfrage des Interrupt-Flag Registers vom
VIC rausfinden.
Im oben gezeigten Startup ist der IRQ-Stack zu klein. Sieh da lieber mal
256 Bytes vor (dein Handler braucht zwar weniger, aber das wird sicher
mal mehr).
Wenn der Controller bei CPSR auf die Nase fällt, dann möglicherweise
weil er genau dann einen Interrupt verpasst bekommt und stecken bleibt.
Für's Debugging per JTAG ist bei LPC2000ern hilfreich, wenn im
Startup-Code als erstes eine mächtige Zählschleife sitzt, die die ersten
paarhundert Millisekunden abläuft, bis der Debugger die CPU nach dem
Reset eingefangen hat. Das ist ein Konstruktionsfehler der LPCs: JTAG
kann den Prozessor nicht sofort aus Reset einfangen, sondern der läuft
unkontrolliert los und irgendwann später kriegt das JTAG ihn zu fassen.
Wenn da die Initialisierung schon durch ist, gibt's allerlei unlustige
Effekte.
Ach ja: Beim LPC2138 ist vor dem ersten RAM-Zugriff eine spezielle
Initialierung erforderlich. Kann sein, dass der LPC2148 das auch
benötigt => Errata Sheet. Siehe irgendwelchen Startup-Codes im Netz, in
manchen wird sich das sicher finden lassen.
> Ich debugge im RAM. Insofern denke ich es passt schon ;-)
Es ist ja nicht so, dass das RAM dann rein garnicht funktioniert.
Sondern dass es in bestimmten Fällen Mist baut.
Merke: Wer ARM-Controller programmiert und verbaut, ohne vorher das
dazugehörende Erratasheet gründlich gelesen zu haben, wird mit
mindestens 3 Jahren PIC16 bestraft! ;-)
EDIT: Das wird sicherlich nichts mit dem Problem hier zu tun haben. Aber
trotzdem wichtig.
> Ich debugge im RAM. Insofern denke ich es passt schon ;-)
Steht im Flash was drin?
Das läuft nämlich dann so: Nach Reset läuft das Teil erst einmal eine
Weile aus dem Flash. Dann fängt JTAG ihn ein und schickt ihn ins RAM.
Wenn der im Flash-Code zu diesem Zeitpunkt schon irgendwas initialisiert
hat, dann kann das deinen schönen Code höllisch durcheinander bringen.
Einfachste Abhilfe: Für RAM-Debugging einmal vorweg einen Code flashen,
der rein garnichts tut (Totschleife ab Reset). Oder aber besagte
Zeitschleife vorneweg hat.
Ach Du Scheiße!
Habe witzigerweise das Errata gelesen, aber falsch abgeschrieben. Hatte
MAMTIM=0x04 gesetzt.
Leider hält mich Eclipse gerade und schon seit ner Weile mit
regelmäßigen Abstürzen vom Testen ab.
Ich könnte mir ja so in den Arsch beissen!
Nachdem Eclipse mich jetzt Ewigkeiten vom Ausprobieren abgehalten hat,
habe ich feststellen müssen, dass es einfach funktioniert!!!
Witzigerweise ist die einzige Änderung das MAMTIM gewesen. Die
PLL-Feed-Sequenz dauert jetzt zwar irgendwie länger, aber das ist mir
jetzt mal echt egal!
Auf jedenfall vielen Dank für Eure Tipps und Hilfe!!!!
Den Stack werde ich gleich mal auf 256 stellen.
Ne weitere Frage habe ich allerdings noch. Und zwar, wie mache ich das
mit der Sicherung der Linkregister? Brauche ich das überhaupt? Möchte
erstmal dahin kommen, dass ich keine Schwierigkeiten bekomme, wenn der
Interrupt vom ADC in die Timerinterruptroutine fällt.
> Ne weitere Frage habe ich allerdings noch. Und zwar, wie mache ich das> mit der Sicherung der Linkregister? Brauche ich das überhaupt? Möchte> erstmal dahin kommen, dass ich keine Schwierigkeiten bekomme, wenn der> Interrupt vom ADC in die Timerinterruptroutine fällt.
Lass das mit dem Nesting sein, bis du ein Stück weiter bist. Die
Interrupt-Routine läuft von haus aus mit abgeschalteten Interrupts das
wird dann also nicht passieren.
> Bisher habe ich noch die PLL-Prozedur davor. Ist besser wenn ich das> vertausche, oder?
Da die Voreinstellung auf "langsamst" steht, ist es eigentlich egal.
So, jetzt ist wieder Doku greifbar. Es geht beim dem Fehler MAM.1 nicht
um MAMTIM, sondern eher um MAMCR. Und das sollte man vorsorglich gleich
machen, also bevor man das RAM anfasst, im Startup. Langsameres
Flash-Timing hingegen sollte erst einmal keine Rolle spielen. Wenn man
allerdings später am MAMTIM drehen will, sollte man beachten, dass man
dies lieber nicht bei eingeschaltetem MAM macht.
Das ist nun für deinen RAM-residenten Code nicht von Belang, aber pack
es lieber trotzdem in den Startup rein. Schadet nicht und erspart Ärger
später.