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.