Forum: Compiler & IDEs NXP LPC2468 interrupt handling mit arm-toolchain


von tm (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

ich verwende einen LPC2468 von NXP mit arm-toolchain. Ich kann Daten 
über uart0 senden und empfangen. Das Empfangen würde ich, anstatt auf 
den Empfangspuffer zu pollen, gerne über einen Interrupt lösen. Leider 
wird die von mir angelegte Interruptroutine einfach nicht angesprungen. 
Ich habe an zahlreichen Stellen die Diskussion zu diesem Thema verfolgt 
und denke dass meine VIC Initialisierung und die Aktivierung des 
Interrupts im uart0 in Ordnung sein müssten.

Ich vermute dass meinem Startupcode ein "Interrupt-Handler" o.ä. fehlt. 
Leider konnte ich bestehende Beispielcodes nicht in mein Projekt 
einbinden ohne sehr viele neue Abhängigkeiten unaufgelöst zu lassen.
Evntl. ist auch die Syntax des Funktionsprototypen für die ISR noch 
nicht ganz GNU-ARM-konform.

Ob in die ISR gesprungen wird teste ich dadurch, dass ich dann sofort 
eine Nachricht über uart0 zurück schicke.

Hat jemand die selbe Konfiguration schonmal zum Laufen gebracht?

Hier die wichtigsten Initialisierungsroutinen. (Die vollständigen 
Quellcode-Dateien habe ich angehängt.)

1
#define IER_RBR    0x01
2
#define IER_THRE  0x02
3
#define IER_RLS    0x04
4
5
void InitUART0()
6
{
7
  PCONP |= (1 << 3);         // UART0 power on
8
  PINSEL0 |= 0x00000050;     // P0.2 TXD0, P0.3 RXD0
9
  U0FDR = 0;                 // Fractional divider not used
10
  U0LCR = 0x83;              // 8 bits, no Parity, 1 Stop bit and enable access to divisor latches
11
  U0DLL = 97;                // 9600 Baud Rate @ 15 MHz VPB Clock
12
  U0DLM = 0;                 // High divisor latch = 0
13
  U0LCR = 0x03;              // DLAB = 0 and enable access to U0IER
14
  PCLKSEL0 &= 0xFFFFFF7F;    // clock selection for UART0
15
  U0FCR = 0x07;              // for UART0: RX and TX FIFO reset and FIFO enable
16
}
17
18
void UART0ISR (void)
19
{
20
  SendString("Character received!\n");
21
  VICVectAddr = 0;           // Acknowledge Interrupt
22
}
23
24
void InitUART0Interrupt()
25
{
26
  U0IER = 0x00;                              // disable all interrupts
27
  VICIntEnClr = 0xFFFFFFFF;                  // delete all interrupts
28
  VICIntSelect |= 0x00000000;                // select uart0 interrupt source as IRQ (not FIQ)
29
  VICVectAddr0 = (unsigned long)UART0ISR;    // address of the ISR
30
  VICVectCntl0 |= ((1<<5) | 6);              // VIC enable and channel 6 (weißt evntl. VICVectAdr0 den uart0-interrupt zu???)
31
  VICIntEnable |= (1<<6);                    // select uart0 in interrupt enable clear register
32
33
  U0IER = (IER_RBR | IER_THRE | IER_RLS | 0x03); // Enable UART0 interrupt
34
}

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

Testweise die folgenden Modifikationen vornehmen:

(1) in crt0.s bei msr CPSR_c, #MODE_SYS|I_BIT|F_BIT die letzten beiden 
Operatoren löschen also: CPSR_c, #MODE_SYS (Kommentar zu der Zeile ist 
fehlleitend aber stört erstmal nicht)

(2) vor void UART0ISR (void) {... noch einen Prototypen mit IRQ-Attribut 
also
void UART0ISR (void) _attribute_ ((interrupt("IRQ")));
(Diese Attribute sollten nach Möglichkeit ohnehin vermieden werden, 
besser: Assembler-Wrapper)

(3) dem Compiler zusätzlich -mapcs-frame also Option mitgeben (CFLAGS), 
falls nicht schon so

(4) in Funktion UART0ISR SendString-Zeile löschen (Katze...Schwanz...) 
und stattdessen eine LED ein- oder umschalten

(5) in UART0ISR vorerst zumindest per dummy die Register U0LSR und U0RBR 
auslesen (...unsigned int dummy; dummy=U0LSR; dummy=U0RBR;...) später 
dann die Register "richtig" auswerten (vgl. u.a. NXP Beispiele f. 
LPC23xx/24xx)

von tm (Gast)


Angehängte Dateien:

Lesenswert?

Hi Martin,

vielen Dank für deine Antwort! Ich habe in den letzten Tagen schon viel 
Inspiration aus deinen Beispielen geschöpft!

Die Änderung in crt.s (nicht crt0.s) habe ich vorgenommen. Den 
Prototypen
1
void UART0ISR (void) __attribute__ ((interrupt("IRQ")));
habe ich ebenfalls eingebaut, allerdings konnte ich die Änderung nur im 
Prototyp vornehmen, nicht in der Signatur der eigenlichen Methode, sonst 
bekomme ich ein "uart.c:54: error: expected ‘,’ or ‘;’ before ‘{’ token
make: *** [uart.o] Error 1"

Meine CFLAGS im Makefile habe ich ebenfalls erweitert:
1
CFLAGS  = -I./ -c -fno-common -O0 -g -mapcs-frame

Aus UART0ISR() habe ich den SendString()-Aufruf entfernt und lasse nur 
noch eine LED mittels Schleife ca. 2 sec. lang aufleuchten. Andere LEDs 
schalte ich in InitUART0() und InitUART0Interrupt() an, um zu sehen ob 
das Programm bis dorthin ausgeführt wird.

Auch die UART0-Register lese ich in UART0ISR() aus, wie von dir 
beschrieben.

Leider leuchtet meine ISR-LED noch nicht, wenn ich zum Test Zeichen auf 
meinem PC in gtkterm eintippe. Die Zeichen müssten aber gesendet werden, 
da ich über die selbe Art und Weise auch ein uCLinux fernsteuern kann.

Als Entwicklungsumgebung verwende ich Eclipse.

Habe ich vielleicht doch einen Wert bei der Initialisierung falsch oder 
garnicht gesetzt? Fehlt mir noch irgend etwas in meinem Startupcode?

Viele Grüße,
tm

von tm (Gast)


Lesenswert?

Update:
1
void __attribute__ ((interrupt("IRQ"))) UART0ISR(void)
 akzeptiert er auch in der Funktionsdefinition selbst. Leider zuckt noch 
immer nichts...

von Frank B. (f-baer)


Lesenswert?

afaik möchte ARM-GCC sowas als Prototyp lesen:

void isr_UART0 ( void ) _IRQ_;

Was mir in deiner ISR noch fehlt, ist ein Löschen der noch nicht 
erledigten UART-Interrupts.

Das eigentliche Problem ist aber, dass du den WDT-Interrupt auf UART0ISR 
lenkst. UART0 ist VICVectAdr6.

von tm (Gast)


Lesenswert?

Hi,

ich habe den Prototyp einfach mal so wie von dir gezeigt in die 
Headerdatei eingebaut. Scheint er aber nicht zu mögen.

Das mit dem Zuweißen der Interruptadressen ist mir eh noch nicht ganz 
klar. Das Manual ist hier nicht sehr ausführlich, leider habe ich bisher 
noch nie mit dem VIC gearbeitet.

Besonders das Wirken der Register VICVectAddr6, VICVectCntrl6 und 
VICVectPriority6 ist mir noch nicht ganz klar.

Besonders weil man in der lpc2468_registers.h folgende Kommentarzeile 
findet:
1
//The name convention below is from previous LPC2000 family MCUs, in LPC230x, these registers are known as "VICVectPriority(x)".
2
#define VICVectCntl0   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x200))
3
#define VICVectCntl1   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x204))
4
#define VICVectCntl2   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x208))
5
#define VICVectCntl3   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x20C))
6
#define VICVectCntl4   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x210))
7
#define VICVectCntl5   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x214))
8
#define VICVectCntl6   (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x218))
9
...

Und das obwohl es in der selben Datei auch einen Block mit VICVectCntl 
gibt. Ich glaube aber beide zeigen auf die exakt gleichen Adressen, 
richtig? Das finde ich aber verwirrend weil im Handbuch zum LPC24xx 
steht:

"VICVectPriority0: Vector priority 0 register. Vector Priority Registers 
0-31. Each of these registers designates the priority of the 
corresponding
vectored IRQ slot."

Die VICVectCntl Register sind dagegen garnicht dokumentiert. Das sagt 
mir dass die mal umbenannt wurden. Ich verstehe nur den neuen Namen 
"VICVectPriority" nicht so ganz, da es ja eigentlich um die Adressen der 
ISR-Routinen geht und nicht um deren Priorität.

von Frank B. (f-baer)


Lesenswert?

Hallo,

deine VICVectCntl-Makros zeigen auf die Priority-Register, richtig.

VICVectPriority sagt nur, welcher Interrupt zuerst abgearbeitet wird, 
wenn mehrere gleichzeitig auftauchen. Das brauchst du übrigens nur für 
IRQ, nicht bei FIQ
.
AFAIK müssen alle Interrupts, die du aktivierst, auch priorisiert 
werden.
Die Priority-Register fangen bei FF FF F2 00 an, die Adress-Register bei 
FF FF F1 00.

Aber wie gesagt, das eigentliche Problem ist das hier:
1
VICVectAddr0 = (unsigned long)UART0ISR;    // address of the ISR
Da müsste stehen
1
VICVectAddr6 = (unsigned long)UART0ISR;    // address of the ISR

Zusatz:
>Besonders das Wirken der Register VICVectAddr6, VICVectCntrl6 und
>VICVectPriority6 ist mir noch nicht ganz klar.

VICVectAddr sind die Register, die die Adresse der ISR enthalten. Löst 
Timer0 einen Interrupt aus, dann springt der PC an die Stelle, die in 
VICVectAddr4 steht.
VICVectPriority sagt etwas über die Reihenfolge der Abarbeitung.
Wenn WDT und Timer0 gleichzeitig kommen, dann wird der Interrupt zuerst 
ausgeführt, der die höhere Priorität besitzt.
Beispiel: VICVectPriority0 = 31 (WDT), VICVectPriority4 = 12 (Timer0)
WDT ist höher priorisiert und wird zuerst ausgeführt.
Deine VICVectCntl-Makros sind gleichbedeutend mit den 
VICVectPriority-Registern.

von tm (Gast)


Lesenswert?

Hi! Klasse vielen Dank für deine Hilfe. Nun springt er rein!! :-)

Was zusätzlich noch gefehlt hatte war die Konfiguration meines FIFOs:
1
#define UFCR_FIFO_ENABLE    (1 << 0)    // FIFO Enable
2
#define UFCR_FIFO_TRIG8     (2 << 6)    // Trigger @ 8 characters in FIFO
3
#define UART_FIFO_8 (unsigned int)(UFCR_FIFO_ENABLE + UFCR_FIFO_TRIG8)
4
U0FCR = UART_FIFO_8;

von Guest (Gast)


Lesenswert?

Hallo tm,

ich habe hier gespannt mitgelesen, da ich mich in Kürze auch mit diesem 
Prozessor befassen möchte.
Wärst Du so nett und hängst hier ein Minimalprojekt für die 
arm-toolchain mit ISR-Beispiel hier rein?
So mit startup etc...
Wäre Super!

von tm (Gast)


Lesenswert?

Klar, kann ich machen. Ich bin gerade dabei das Projekt etwas 
aufzuräumen und sauber zu implementieren. Im moment geht es zwar, ist 
aber ziemlich wild. Sobald ich fertig bin werde ich ein komplettes 
Beispielprojekt für meine Konfiguration veröffentlichen.

Vielleicht kann mir unter dessen jemand weiterhelfen was das Konstrukt 
"1UL" bzw. "3UL" genau bedeutet, was es macht oder woher es kommt. Habe 
keine Informationen dazu gefunden. Ich denke es muss irgendwie zum 
C-Sprachumfang gehören... 1UL scheint ein Bitmuster der Form "...0001" 
zu erzeugen und 3UL ein Bitmuster der Form "...0011". Wer weiß wo ich 
dazu mehr Informationen bekomme?

Dieser Ausdruck wird nämlich sehr geschickt in einem Codeschnipsel von 
Martin Thomas benutzt:
1
    
2
PINSEL0 = (PINSEL0 & ~(3UL<<4)) | (1UL<<4);
3
PINSEL0 = (PINSEL0 & ~(3UL<<6)) | (1UL<<6);

von tm (Gast)


Lesenswert?

Ich habs raus: 1UL bedeutet einfach nur die Konstante 1 als unsigned 
long (32 Bit), das entspricht dem Bitmuster ...0001
3UL bedeutet die Konstante 3 als unsigned long, das entspricht dem 
Bitmuster ...0011

:-)

von Frank B. (f-baer)


Lesenswert?

Das geht im übrigen einfacher, wenn man gleich als HEX-Zahl deklariert.
1
(uint32_t)(0x3<<6)
hat den selben Effekt und ist bezüglich der Bit-Darstellung deutlich 
klarer.
1
PINSEL0 = (PINSEL0 & ~(uint32_t)(0x3<<4)) | (uint32_t)(0x01<<4);
2
PINSEL0 = (PINSEL0 & ~(uint32_t)(0x3<<6)) | (uint32_t)(0x01<<6);

Sicherlich etwas länger, aber dafür übersichtlich.

@guest: Schau dir mal Starteasy for ARM an. Damit kann man für die 
LPC-Reihe ein fertiges Musterprojekt erstellen, die meisten Interrupts 
vorkonfigurieren und der Startup-Code wird auch gleich erstellt. Das 
wird dann zwar als HiTOP-Projekt erstellt, aber es zählt ja sowieso nur 
der Code.

von tm (Gast)


Lesenswert?

Da stellt sich mir wieder gleich die Frage wo uint32_t definiert wird. 
:-D Habe es schon mehrfach in Beispielprojekten gesehen konnte es aber 
nie bis zu seiner Deklaration zurückverfolgen. Zu C gehört es jedenfalls 
nicht, glaube ich. In meinen Projekten steht mir dieser Typ nicht zur 
Verfügung.

von (prx) A. K. (prx)


Lesenswert?

uint32_t => stdint.h => C99.

von (prx) A. K. (prx)


Lesenswert?

Frank Bär schrieb:

> Sicherlich etwas länger, aber dafür übersichtlich.

Welche Vorteil hat in diesem Zusammenhang die vielfache Verwendung von 
uint32_t? Und warum ist hier Hex einfacher als Dezimal?

von tm (Gast)


Lesenswert?

Leider habe ich mich zu früh gefreut. Meine LED blinkt zwar bei jedem 
Zeichen das ich sende aber bei genauerem hinsehen habe ich festgestellt, 
dass er niemals in die ISR springt, sondern komischerweise in die 
InitUART0ISR!! Und nein ich habe nicht die falsche Adresse in 
VICVectAdr6 geschrieben. Noch verworrener wird das ganze, wenn ich 
zwecks Debugausgabe in den einzelnen Methoden ein bisschen herumspiele. 
Dann macht er nach jedem empfangenen Zeichen sogar einen kompletten 
Reset!
Weiß leider überhaupt nichtmehr weiter. Ich denke mir, dass ich 
vielleicht doch so einen Interrupt-Wrapper brauche...? Kann es sein, 
dass das Blinking-LED-Projekt von Olimex garnicht für Interrupts 
ausgelegt ist und mir da noch etwas fehlt? Vor allem was das Anspringen 
des richtigen Vectors angeht?

von Guest (Gast)


Lesenswert?

Frank Bär schrieb:
> @guest: Schau dir mal Starteasy for ARM an...

Danke für den Tipp! Sieht auf den ersten Blick aus, als sei es genau das 
was ich suchte.
Wird das Tool noch gepflegt? LPC17xx vermisse ich...

von Frank B. (f-baer)


Lesenswert?

A. K. schrieb:
> Frank Bär schrieb:
>
>> Sicherlich etwas länger, aber dafür übersichtlich.
>
> Welche Vorteil hat in diesem Zusammenhang die vielfache Verwendung von
> uint32_t? Und warum ist hier Hex einfacher als Dezimal?

Weil die Bitdarstellung von 0x23 eindeutig ist, bei 35 dagegen nicht. 
Mag Geschmackssache sein. Ich arbeite lieber durchweg mit Hex-Werten, 
weil das auch die Darstellung ist, die sich im Debug-Modus in den 
Registern wiederfindet. Die vielfache Verwendung des typecasts, weil der 
ARM-GCC meiner Erfahrung nach ziemlich zickig sein kann und teilweise 
völlig unklar kompiliert.

@tm: Schau mal, ob der Compiler die richtigen Sprungadressen in 
VICVectAddr6 hinterlegt, eventuell hilft es, die Code-Optimierungsstufe 
mal zu ändern. Der ARM-GCC hat sich da teilweise ziemlich zickig, wie 
ich feststellen durfte. Manche Fehler sind in der Übersetzung begründet, 
warum sie auftreten, ist mir nach langem rumprobieren immernoch so 
unklar, wie am Anfang.

@Guest: Starteasy wird, soweit ich das beurteilen kann, noch gepflegt, 
ja. Mit ARM-Cortex habe ich in der Toolchain noch nicht gearbeitet, 
daher kann ich dir dazu wenig sagen, möglicherweise hilft es, mal den 
Support von Hitex anzuschreiben.

von tm (Gast)


Lesenswert?

So jetzt gehts zuverlässig! Es lassen sich allerdings bisher immer nur 
acht Zeichen (exakt die Größe meines FIFO-Buffers) lesen bevor kein 
Interrupt mehr ausgelöst wird. Weiß jemand einen Rat?

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.