Forum: Mikrocontroller und Digitale Elektronik Problem mit Receive-Interrupt des CAN-Controllers


von Norman M. (norman)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

Ob es wirklich zielführend ist, den IRQ-Handler in der Sprungleiste 
auszukommentieren? Ansonsten ist mit diesen Codeschnipseln nichts 
anzufangen.

von Norman M. (norman)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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.

von Norman M. (norman)


Lesenswert?

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?

von Andreas K. (a-k)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?


von Andreas K. (a-k)


Lesenswert?

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.

von Norman M. (norman)


Lesenswert?

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...

von Andreas K. (a-k)


Lesenswert?

Letzter Versuch: Rück den Code raus!

von Norman M. (norman)


Lesenswert?

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*)&timestamp_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?

von Andreas K. (a-k)


Lesenswert?

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.

von Norman M. (norman)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Norman M. (norman)


Lesenswert?

ich habe die Routine so als IRQ deklariert:

void IRQ_CAN1Rx(void) _attribute_ ((interrupt("IRQ")));

War das die Antwort auf deine Frage?

von Andreas K. (a-k)


Lesenswert?

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.

von Norman M. (norman)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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?

von Norman M. (norman)


Lesenswert?

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
Noch kein Account? Hier anmelden.