www.mikrocontroller.net

Forum: Compiler & IDEs LPC2378 interrupt?


Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich will auf einem LPC2378 den CAN über interrupt laufen lassen, die 
Beiträge hier im Forum haben mir leider noch nicht weiterhelfen können. 
Meine Initialisierung sieht wie folgt aus :
...
CAN1IER = CAN2IER = 0x01; // Enable receive interrupts
...
VICVectAddr23 = HandlerAddr //HandlerAddris Pointer auf function
VICVectCntl23 = 0x01;
VICIntEnable = 1 << 23;  /* Enable Interrupt */
...

nun dacht ich, es muss noch das I-Flag im CPSR vom ARM-Kern deaktiviert 
werden daher steht in meinem Startup-File:
/* 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, 0X00000080/* 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) */

[....]

/* 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|F_BIT  /* User Mode */
      mov   sp, r0

Problem ist nun: wenn mein Interrupt kommt bleibt der µC stehen
die Interrupt-Routine wird nicht ausgeführt.
Auf VICVectAddr23 steht 0x01B0, genau an dieser Speicheradresse befindet 
sich laut Mapfile die Routine.

Weis einer was ich falsch mache? muss ich den ARM-Kern lieber in einem 
anderem Modus laufen lassen? momentan ist der System mode eingestellt.

hoffe auf eure Hilfe ich bin am verzweifeln!!!

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zeig mal bitte den kompletten Startup-Code. Es könnte sein, dass da was 
falsch eingestellt ist.

>Auf VICVectAddr23 steht 0x01B0, genau an dieser Speicheradresse befindet
>sich laut Mapfile die Routine.

Warum setzt Du die Adresse manuell ein und überlässt es nicht dem 
Compiler/Linker?? Die Adresse kann sich bei der geringsten Änderung des 
Codes ändern.
VICVectAddr23 = (unsigned long)(Funktionsnahme);

>.set  IRQ_STACK_SIZE, 0X00000080/* stack for "IRQ" normal interrupts is 4 bytes*/
128 Bytes für Stack ist etwas wenig, wenn die ISR weitere Funktionen 
aufruft, könnte es knapp werden. 1 kB wär da schon besser.

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo und danke schon mal für die Hilfe!

> Warum setzt Du die Adresse manuell ein und überlässt es nicht dem
> Compiler/Linker??
Mach das schon so wie du sagst, wollt nur damit ausdrücken das meine 
VIC-Ini wohl richtig funktioniert.

Mittlerweile bin ich ein Stück weiter, im Startupcode hab ich die 
I-Bit’s Stehen lasen damit es keine Probleme bei der Initialisierung 
gibt. In meiner Main-Funktion enable ich dann die Interrupts über 
Assembler:
#define IRQ_MASK 0x00000080
static inline unsigned asm_get_cpsr(void)
{
  unsigned long retval;
  asm volatile (" mrs  %0, cpsr" : "=r" (retval) : /* no inputs */  );
  return retval;
}
static inline void asm_set_cpsr(unsigned val)
{
  asm volatile (" msr  cpsr, %0" : /* no outputs */ : "r" (val)  );
}
unsigned enableIRQ(void)
{
  unsigned _cpsr;

  _cpsr = asm_get_cpsr();
  asm_set_cpsr(_cpsr & ~IRQ_MASK);
  return _cpsr;
}

Startupcode:
/* Stack Sizes */
.set  UND_STACK_SIZE, 0x00000080    /* stack for "undefined instruction" interrupts is 4 bytes  */
.set  ABT_STACK_SIZE, 0x00000080    /* stack for "abort" interrupts is 4 bytes                  */
.set  FIQ_STACK_SIZE, 0x00000080    /* stack for "FIQ" interrupts  is 4 bytes               */
.set  IRQ_STACK_SIZE, 0X00000200    /* stack for "IRQ" normal interrupts is 4 bytes          */
.set  SVC_STACK_SIZE, 0x00000080    /* 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

.section .startup, "ax"

_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, [PC, #-0x0120]  // Vector from VicVectAddr
                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:  

        .extern TargetResetInit
                 ldr     SP, =_stack_end    @ temporary stack at Stack_Top
                 LDR R0, =TargetResetInit
                 MOV LR, PC
                 BX R0
        
        /* 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
//Enter Undefined Instruction Mode and set its Stack Pointer
          msr   CPSR_c, #MODE_UND|I_BIT|F_BIT
          mov   sp, r0
          sub   r0, r0, #UND_STACK_SIZE

//Enter Abort Mode and set its Stack Pointer
          msr   CPSR_c, #MODE_ABT|I_BIT|F_BIT
          mov   sp, r0
          sub   r0, r0, #ABT_STACK_SIZE

//Enter FIQ Mode and set its Stack Pointer
          msr   CPSR_c, #MODE_FIQ|I_BIT|F_BIT
          mov   sp, r0  
           sub   r0, r0, #FIQ_STACK_SIZE

//Enter IRQ Mode and set its Stack Pointer
          msr   CPSR_c, #MODE_IRQ|I_BIT|F_BIT
          mov   sp, r0
          sub   r0, r0, #IRQ_STACK_SIZE

//Enter Supervisor Mode and set its Stack Pointer
          msr   CPSR_c, #MODE_SVC|I_BIT|F_BIT
          mov   sp, r0
          SUB   sl, sp, #SVC_STACK_SIZE
          //sub   r0, r0, #SVC_STACK_SIZE

//Enter User Mode and set its Stack Pointer
        //  msr   CPSR_c, #MODE_SYS|I_BIT|F_BIT
        //  mov   sp, r0

      
// copy .data section (Copy from ROM to RAM)
                ldr     R1, =_etext
                ldr     R2, =_data
                ldr     R3, =_edata
LoopRel:       cmp     R2, R3
                ldrlo   R0, [R1], #4
                strlo   R0, [R2], #4
                blo     LoopRel

// Clear .bss section (Zero init)
                mov     R0, #0
                ldr     R1, =_bss_start
                ldr     R2, =_bss_end
LoopZI:      cmp     R1, R2
                strlo   R0, [R1], #4
                blo     LoopZI

        /* Enter the C code  */
                b       main
.endfunc
.end

Der Interrupt am ARM-Kern bewirkt auch das er zum richtigen Exception 
Vector an Stelle 0x0018 springt, also zu der Zeile:
"ldr     PC, [PC, #-0x0120]  "ab hier denk ich läuft es schief.
Ziel ist ja dem Kern die Adresse der Interrupt-Routine mitzuteilen die 
im VicVectAddr (0xFFFFFF00) steht. Die Aktion [PC, #-0x0120] springt 
aber meiner Meinung falsch oder?
PC ist an dieser Stelle 0x18(befehlsanfang)+0x02 also 0x20
0x20-0x0120 ist bei mir 0xFFFFFEFF müsste man nicht 0x011F abziehen?
0x20-0x011F = 0xFFFFFF00

PS:
meine Interrupt-Routine habe ich so deklariert:
void CAN_Handler(void)
{...
return;
}
void CAN_Handler(void)  __attribute__ ((interrupt ("IRQ")));

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
hab mir das Disassembly der Zeile PC, [PC, #-0x0120] angeschaut.
angeblich versteht der gcc-Assembler das nicht...
allerdingst macht mein Debugger auch blödsin wenn er asm-befegle bekommt

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ziel ist ja dem Kern die Adresse der Interrupt-Routine mitzuteilen die
>im VicVectAddr (0xFFFFFF00) steht. Die Aktion [PC, #-0x0120] springt
>aber meiner Meinung falsch oder?

Also meinem Taschenrechner zufolge ist 0x20 - 0x120 = 0xFFFFFF00, was ja 
die Adresse von VICAddress ist. Demnach müsste das so schon richtig 
sein.

Was genau steht denn in CAN_Handler drin? Der ARM7 hat eine Art 
Designfehler, sodass z.b. Interrupts in einer ISR nicht ohne weiteres 
wieder aktiviert werden dürfen. Kannst Du den ganzen Code in ein Archiv 
packen und hier anhängen? Manchmal sinds die Details, an den alles 
scheitert.

MfG Mark

Autor: Steffen H. (mcst)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Naja wenn ich einfach nur 0x20-0x0120 rechne ist das Ergebnis 
0xFFFFFFFFFFFFFF00 stimmt.
Ich habs aber im Kopf gemmacht und hab Füberlauf vergessen das zwischen 
0x0000 und 0xFFFFFFFF eine Stelle unterscheid ist ~schäm~

OK ich hab einfach mal mein ganzes Projekt zuamengepakt und angehangen. 
ist für Eclipse mit GNUARM. Hoff echt du findest den Fehler... ich weis 
sonst nich weiter da der PC ja eigentlich zu VICAddress springt :(

PS: bekommt der µC dann automatisch mit das er weiter zum CAN_Handler 
springen soll?

UND ECHT GROßEN DANK für deine Mühe!

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>bekommt der µC dann automatisch mit das er weiter zum CAN_Handler
>springen soll?

Ja, die Adresse des Handlers wird ja ins VICVectAddr23 geschrieben, was 
für den CAN-Vektor steht. Dann wird der CAN-Interrupt aktiviert. (macht 
in Deinem Fall install_irq() aus irq.c). Sobald der VIC einen Interrupt 
erkennt (z.b. Nr. 23 für CAN), kopiert er den Inhalt des entsprechenden 
VICVectAddr-n nach VICVectAddr und meldet der CPU einen Interrupt 
Request. Diese kopiert den aktuellen PC-Wert ins Link-Register LR des 
IRQ-Modes, schaltet in den IRQ-Mode und setzt den PC auf 0x18, springt 
also zum IRQ-Handler. Dieser springt zu der Adresse, die sich in der 
Speicherzelle [PC - 0x120], also im VICVectAddr, befindet. Im deinem 
Fall ist es eben die Adresse von CAN_Handler.

Hab den Code mal durchgeguckt. Von der Initialisierung des VIC sollte 
das schon richtig sein. Was aber auffällig ist, dass in der main die 
Variable 'CAN2RxDone' auf TRUE abgefragt wird, im CAN_Handler() jedoch 
'CAN1RxDone' gesetzt wird. 'CAN2RxDone' wird nirgendswo gesetzt. Vllt 
ist das ja der Fehler.

Was auch problematisch sein könnte, ist dass
void CAN_Handler(void)  __attribute__ ((interrupt ("IRQ")));
 erst nach der Funktionsdefinition kommt. Bin mir aber nicht sicher, ob 
das wirklich einen Unterschied macht.

Woran erkennst Du eigentlich, dass der Controller stehen bleibt? Was 
sollte passieren, wenn alles richtig laufen würde?

Und noch was. In Deinem Programm werden Warteschleifen für die 
LED-Ansteuerung durch Hochzählen realisiert. Das kann bei 
eingeschalteter Optimierung Probleme geben, da es dem Compiler erlaubt 
ist, die Schleife komplett zu entfernen, weil sie ja nichts macht. 
Normaleweise macht man sowas mit Timern oder inline-asm. Als 
Einfachst-Lösung kann man auch die Zählvariable volatile deklarieren, 
dies sagt dem Compiler, dass er keine Optimierungen vornehmen darf.

MfG Mark

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mmmm...
Eigentlich soll er denn zur Adresse aus VICVectAddr springen sprich zum 
CAN_Handler. Dieser aktiviert als erstes eine LED damit ich weis das er 
im Interrupt ist. nach abarbeitung der Routine soll er zur main 
zurückkeren. in der main lass ich eine zweite LED blinken damit ich weis 
das der µC läuft.

Pasieren tut aber meine Meinung nach folgendes:
nach Anliegen des des Interrupts am ARM-Kern schaltet der µC in den 
IRQ-Mode und setzt den PC auf 0x18 wie gewünscht. Ab hier gehts scheif. 
Weder die LED für den Interrupt leuchtet auf noch blinkt meine Zweite 
LED weiter. Im Debuggmode springt er wahllos umher.
Woher weis der µC eigentlich das in VICVectAddr eine Sprungadresse 
steht?

Das mit dem CAN2RxDone hab ich auch gefunden und behoben. Wenn ich die 
Interrupt-Routine über Polling aufrufe funktioniert es wie gewünscht.
Als Interrupt geht es weiterhin schief.

Kann es sein das der Assambler den Befehl "ldr PC, [PC, #-0x0120]" 
falsch übersetzt, da ich eine etwas veraltete GNUARM-Toolchain verwende?
Bisher hatte ich keine Probleme.

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
wenn der µC in den IRQ-Mod springt, springt er da auch in einen anderen 
Sack? kan dies zu meinem problem führen?

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In dem Startup-Code aus dem Rar-Archiv steht
 ldr     PC, [PC, #-0x011F]  // Vector from VicVectAddr 
 . Das ist so falsch, weil das ja 0x120 und nicht 0x11F heißen sollte.

>Woher weis der µC eigentlich das in VICVectAddr eine Sprungadresse
>steht?
Der VIC kopiert bei einem Interrupt Request das entsprechende 
VICVectAddr-n Register (hier VICVectAddr23) automatisch ins Register 
VICAdress, das sich an der Stelle 0xFFFFFF00 befindet. Das heisst die 
Adresse der ISR steht in VICAdress, sobald die CPU nach 0x18 springt.
[c]ldr     PC, [PC, #-0x0120][c] heisst, dass der PC mit dem Wert der 
Speicherzelle geladen werden soll, dessen Adresse sich ergibt, wenn man 
zum aktuellen PC-Wert (in diesem Fall 0x20 weil der Befehl auf 0x18 
steht und der PC wegen der Pipeline immer um 2 Befehle, also 8 Bytes, 
vorauseilt) den Zahlenwert -0x120 addiert. D.h. die CPU soll an die 
Stelle springen, dessen Wert sich in der Speicherzelle 0xFFFFFF00, also 
im VICAdress-Register befindet.

Dass der Assembler das falsch übersetzt kann man ausschließen, da die 
Schreibweise von ARM so vorgegeben ist und es sie schon seit mehr als 
15J gibt.

MfG Mark

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>wenn der µC in den IRQ-Mod springt, springt er da auch in einen anderen
>Sack? kan dies zu meinem problem führen?

Ja, (fast) jeder 7 Modes hat einen eigenen Stack. Alle Stacks werden im 
Startup-Code initialisiert. Siehe den Code nach dem 
'Reset_Handler'-Label in crt.S.

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK hab das geändert aber der erfolg bleibt noch aus.
Da im VICAdress genau die Adresse der CAN_Handler-Funktion steht (hab 
das überprüft) müsst er zumindestens dahin springen. Vll. liegt es ja 
dann daran das ich den CAN_Handler falsch deklariert habe...

Autor: Steffen H. (mcst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
HEy noch mal ich,
nachdem ich am WE mein Linkerskript und den Startupcode nochmal 
überarbeitet habe funzt es jetzt mit den Interrupts! :)

Was allerdings genau falsch war hab ich bis jetzt noch nicht 
rausgefunden.
Dake für die Hilfe!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.