Hallo,
ich schreibe seit einiger Zeit an einem Programm für mein Olimex Board
LPC-L2294. Es werdem dem MC per Ethernet in Paketen CAN-Nachrichten
geschickt, diese im Speicher abgelegt und nach und nach auf den Bus
versendet. Das funktioniert sehr gut.
Außerdem sollen aber auch CAN-Nachrichten vom Bus empfangen werden. Und
hier liegt das Problem.
Beim Debuggen, habe ich festgestellt, dass bei der ersten erhaltenen
CAN-Nachricht, der Interrupt richtig angezogen und die Service-Routine
aufgerufen wird. Dann aber wird die Routine scheinbar nicht mal bis zum
Ende abgearbeitet. Stattdessen wird ein Dummy-Service-Routine
void UNDEF_Routine (void)
{
//while (1) ;
}
aufgerufen. Was danach so richtig passiert kann ich nicht
nachvollziehen. Das Programm scheint dann normal weiter zu laufen. Das
Ende der Receive-Interrupt-Service-Routine wird aber nie erreicht.
Also um das mal ein wenig zu konkretisieren:
Im StartupCode wird folgendes angegeben (Habe ich aus einem
Beispielprogramm übernommen, und bisher auch keine Probleme damit
gehabt):
_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 */
Da ist eben auch die oben erwähnte Dummmy-Routine mit dabei. Leider habe
ich nicht so ganz deren Sinn verstanden. Kann mir das jemand erklären?
Der Receive-Interrupt ist wie folgt konfiguriert (Bsp. Kanal 1):
void init_can1Rx_interrupt(void)
{
CAN1IER = 0x1; //Enable CAN1 Receive-interrupt
VICIntSelect &= 0xfbffffff; // CAN1 Rx IRQ
VICVectAddr4 = (unsigned int)IRQ_CAN1Rx;
VICVectCntl4 = 0x3A; // Enable vectored interrupt CAN1 Rx.
VICIntEnable = 0x4000000;
CAN1GSR = 0;
}
In der Routine selbst wird hauptsächlich die Nachricht aus dem
Receivebuffer gelesen und soll im Speicher abgelegt werden (Das wird
aber schon nicht mehr gemacht, dass Auslesen jedoch schon).
Hat jemand eine Idee wo das Problem liegen könnte? Warum springt er in
diese UNDEF-Routine?
Werden noch weitere Infos benötigt, poste ich die gerne auf Anfrage
nach.
Danke vorab für eure Hilfe!
Gruß,
Norman
Ob es wirklich zielführend ist, den IRQ-Handler in der Sprungleiste auszukommentieren? Ansonsten ist mit diesen Codeschnipseln nichts anzufangen.
Was genau macht den der IRQ-Handler? Ich hatte den auskommentiert, da das ja auch nur eine Dummy-Routine war (nach meinem Verständnis, und beim Vectored Interruptcontroller ja die Adresse der entsprechenden Service-Routine angegeben wird und diese ja dann auch angesprungen werden soll). Wozu wird diese Dummy-Routine dann noch gebraucht? Und kann das was mit meinem Problem zu tun haben? Welcher Code wird denn noch benötigt? Gruß, Norman
An diese Stelle der Sprungleiste springt die CPU wenn ein Interrupt aufläuft. Und davon, dass du das auskommentierst, weiss sie nichts und springt trotzdem dorthin. Und erwischt dann eben das, was nun dort steht, also den FIQ Handler.
Es gibt von Hitex einen Wälzer über NXPs ARM7er, den man sich auch runterladen kann (zur Strafe allerdings ohne Inhaltsverzeichnis). Könnte sich für dich lohnen, da mal reinzusehen.
Ok danke, da kann ich ja mal reinschauen. Aber mich wurdert trotzdem schon, dass er dann nicht in die FIQ-Routine springt, sondern in die UNDEF-Routine. Und wieso tut er das überhaupt, wenn er doch schon in der richtigen Routine ist?
Irgendwie werde ich das Gefühl nicht los, dass dieser Thread gleich in eine längere Anfänger-Vorlesung in das Wesen der Interrupt-Verarbeitung von ARM7 Prozessoren ausartet.
Norman M. wrote: > Aber mich wurdert trotzdem schon, dass er dann nicht in die FIQ-Routine > springt, sondern in die UNDEF-Routine. Möglicherweise macht er erst das eine, dann das andere. Nach UNDEF läuft er hier beispielsweise, wenn er auf eine Adresse zugreift, an der sich nichts befindet. Warum er das vielleicht tut ist deinen Schnipseln nicht zu entnehmen.
Naja ich habe mich ja schon mit der Thematik anhand des User-Manuals beschäftigt. Mit den Timer-Interrupt klappt ja auch alles und das sind auch IRQs. Nur das Problem hier verstehe ich nicht. In dem pdf lese ich gerade mal...
ok ok ;)
Also die Receive Routine sieht wie folgt aus:
void IRQ_CAN1Rx(void)
{
lpc2000CANdriver_RXObj_t s_can1_rcv_data;
unsigned int timestamp_ui, can_msg_dlc_ui;
unsigned char flags_uc, *recv_can_msg_write_pos_alt_ucp;
//aktuellen Timestamp sichern
timestamp_ui = T1TC;
//Auslesen der CAN-Nachricht aus den Registern des CAN-Controllers 1
lpc2000CANdriver_ReceiveMessageCh1 (&s_can1_rcv_data);
//Kontrolle des Speicherplatzes + Sicherung alte Position des Pointers
recv_can_msg_write_pos_ucp
recv_can_msg_write_pos_alt_ucp = recv_can_msg_write_pos_ucp;
can_msg_dlc_ui = (s_can1_rcv_data.RFS & 0x000F0000) >> 16;
//CAN-DLC speichern
if((RECV_CAN_MSG_END_UCP - recv_can_msg_write_pos_ucp) < 20)
{
g_recv_can_msg_data_end_ucp = recv_can_msg_write_pos_ucp;
//Datenende am Ende des Speichers abspeichern
recv_can_msg_write_pos_ucp = (char*)RECV_CAN_MSG_START_UCP;
//Pointer auf Startadresse des Speicherbereichs
}
//Prüfung auf Datenverlust
if(recv_can_msg_write_pos_ucp < recv_can_msg_read_pos_ucp)
{
if((recv_can_msg_write_pos_ucp + 12 + can_msg_dlc_ui) >=
recv_can_msg_read_pos_ucp)
{
g_data_lost_flag_usi = 1; //Datenverlust eingetreten
return;
}
}
if(recv_can_msg_write_pos_alt_ucp != recv_can_msg_read_pos_ucp)
{
if(recv_can_msg_write_pos_alt_ucp < recv_can_msg_read_pos_ucp)
{
g_data_lost_flag_usi = 1; //Datenverlust eingetreten
return;
}
}
//kopiere CAN-Nachricht in ext. RAM
*recv_can_msg_write_pos_ucp = 0x01;
//Nachrichtennummer (MSB)für CAN-Nachricht auf Kanal 1 an Offset 0
*(recv_can_msg_write_pos_ucp+1) = 0x00;
//Nachrichtennummer (LSB)für CAN-Nachricht auf Kanal 1 an Offset 1
memmove(recv_can_msg_write_pos_ucp+3,(const void*)×tamp_ui,4);
//kopiere Timestamp an Offset 3
memmove(recv_can_msg_write_pos_ucp+7,(const
void*)&s_can1_rcv_data.ID,4); //kopiere CAN-ID an Offset 7
flags_uc = 0x00;
flags_uc |= ((s_can1_rcv_data.RFS & 0x80000000) >> 30);
//Filtere IDE-Bit und kopiere in flags (Bit 1)
flags_uc |= ((s_can1_rcv_data.RFS & 0x40000000) >> 30);
//Filtere RTR-Bit und kopiere in flags (Bit 0)
flags_uc |= ((s_can1_rcv_data.RFS & 0x000F0000) >> 14);
//Filtere DLC und kopiere in flags (Bits 5:2)
*(recv_can_msg_write_pos_ucp+11); //kopiere flags
an Offset 11
if(can_msg_dlc_ui > 4)
{
memmove((send_can_msg_read_pos_ucp+12),(const
void*)&s_can1_rcv_data.DataField[0], 4); //kopiere Datenbytes 0-3 an
Offset 12
memmove((send_can_msg_read_pos_ucp+16),(const
void*)&s_can1_rcv_data.DataField[1], (can_msg_dlc_ui) - 4); //kopiere
Datenbytes 4-x (x>=7)an Offset 16
}
else
{
memmove((send_can_msg_read_pos_ucp+12),(const
void*)&s_can1_rcv_data.DataField[0], (can_msg_dlc_ui) - 4); //kopiere
Datenbyte 0-x (x<=3)an Offset 12
}
*(recv_can_msg_write_pos_ucp+2) = (char)(9 + can_msg_dlc_ui);
//Länge der Nachricht (Timestamp, CAN-ID, flags, Signale) an Offset 2
//Pointer an auf Adresse nach abgelegter Nachricht weiterschalten
recv_can_msg_write_pos_ucp += (12 + can_msg_dlc_ui);
//Bytes auf Datenzähler addieren
g_recv_data_length_ui += (12 + can_msg_dlc_ui);
CAN1CMR = 0x04;
VICVectAddr = 0x00;
}
Sieht jetzt ein wenig unübersichtlich aus wegen der Formatierung.
Die Pointer liegen auf folgenden Adressen:
recv_can_msg_write_pos_ucp = (char*)RECV_CAN_MSG_START_UCP;
recv_can_msg_read_pos_ucp = (char*) RECV_CAN_MSG_START_UCP;
wobei
#define RECV_CAN_MSG_START_UCP ((volatile unsigned char *) 0x8104B001)
#define RECV_CAN_MSG_END_UCP ((volatile unsigned char *) 0x810C8001)
gilt.
Reicht das schon, oder ist noch mehr von Nöten?
Ich dache mehr an den kompletten Startup-Code. Du solltest am Anfang eines nicht funktionierenden Handler mal eine LED einschalten, oder sowas in der Art. Denn dass du letztlich im UNDEF landest, heisst nicht dass du gleich dort landest. Evtl. landest du wie schon erwähnt über Umweg dort.
Ok, danke für den Tipp, das könnte man mal ausprobieren. Leider kann ich
das erst wieder am WE richtig probieren, weil ich hier den CAN nicht
testen kann.
Der Startup-Code sieht wie folgt aus:
/*
************************************************************************
***************************************
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, 0X00000100 /* stack for "IRQ" normal interrupts
is 4 bytes */
.set SVC_STACK_SIZE, 0x00000014 /* 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) */
/*Interrupt global enablen*/
/*.set I_BIT, 0x00 when I bit is set, IRQ is
disabled (program status registers) */
/*.set F_BIT, 0x00 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
Ok, sorry, ich hatte die Sprungleiste und die Vektorleiste verwechselt. Die auskommentierte IRQ_routine geht hier in Ordnung, weil sowieso nicht verwendet. Ist dein CAN IRQ Handler eigentlich vom Funktionstyp ein IRQ Handler? Als normale C Funktion kann das so nicht funktionieren.
ich habe die Routine so als IRQ deklariert:
void IRQ_CAN1Rx(void) _attribute_ ((interrupt("IRQ")));
War das die Antwort auf deine Frage?
Ja, ist es. Dann ist also klassisches Debugging angesagt. Entweder mit JTAG. Oder mit der beschriebenen LED sehen, wie weit er im Handler kommt. Mit Debugger ist es etwas leichter. Denn idealerweise hat man als UNDEF Handler eine Totschleife, und wenn man dort landet, dann zeigt R14 an wo es gekracht hat.
Ich hatte schon mal so etwas ähnliches wie oben beschrieben ausprobiert. Er kommt noch bis vor die memmoves. Aber danach kommt er nicht mehr an. Ich hatte aber nicht zwischen den memmoves probiert.
Na also, das ist doch schon dicht dran. Allerdings komme ich mir etwas verarscht vor. Du bist also selber schon soweit, dass es offenbar an spezifischen Teilen der Speicherverarbeitung deines Codes liegt. Daraufhin postest du eine Frage, die nur 2 Schnipsel über die Interrupt-Verarbeitung im Allgemeinen enthält. Wie stellst du dir das eigentlich vor?
Ich will hier ganz bestimmt niemanden verarschen... ;) Das Problem ist, dass ich leider nicht über die memmoves schrittweise debuggen kann, da er da irgendwie das memmove angeblich nicht findet. Dieses Problem habei ich bei jedem memmove in meinem Programm. Dadurch kann ich leider den weiteren verlauf nicht mehr nachvollziehen und sehe halt nur, dass ich plötzlich in der UNDEF-Routine bin. Aber die memmoves schaue ich mir auf jeden Fall nochmal genauer an. Du hattest ja oben schonmal geschrieben, dass es sein kann, dass auf Speicher zugegriffen wird, wo nix steht. Vielleicht habe ich da tatsächlich noch einen Fehler drin. Also sorry, die frage ging vielleicht etwas falsch los, ich wusste es einfach nur nicht besser. War einfach ratlos...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.