www.mikrocontroller.net

Forum: Compiler & IDEs Funktion aus Interrupt aufrufen


Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich versuche grade, mit GCC ein kleines Programm für ARM (LPC2378) zu 
compilieren.
Das Programm verfügt über eine Interruptfunktion namens tick, die von 
einem Timer (Timer0) periodisch alle 1 ms aufgerufen wird.
So, der Interrupt und der Timer funktionieren föllig einwandfrei.
Jetzt möchte ich aber aus der Interruptfunktion heraus (also aus tick) 
eine andere Funktion aufrufen.
etwa so:

void tick(void) _attribute_ ((interrupt("FIQ")));

void tick(void)
{
  fTickHandler();
  T0IR = 0xFF;
  VICAddress = 0;
}

Das ganze funktioniert, aber nur, wenn ich es im RAM ausführe. Sobald 
ich es ins Flash lade, geht mein ganzes Programm nicht mehr.
Der GCC gibt auf jeden Fall eine Warnung aus:

Warning: writeback of base register is UNPREDICTABLE

Frage:
Was bedeutet das? Wie kann ich es beheben? Und warum funktioniert mein 
Programm nicht, obwohl es eigentlich einwandfrei ist?

Ich hoffe ihr könnt mir helfen.
Vielen Dank schon mal im Voraus!


PS:
Ach ja, mir fällt noch was ein.
Ich hatte (von einem früheren Experiment her) mal versehentlich folgende 
Code-Zeile stehen lassen:

void tick(void) _attribute_ ((interrupt("FIQ")));

anstatt

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

Das Programm hat aber in beiden Fällen funktioniert. Warum?

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann niemand was dazu sagen?

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist es so schwer die Fehlermeldung in Google einzugeben ??

http://ccgi.rowley.co.uk/support/faq.php?do=articl...

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das habe ich schon gefunden.
Das Problem wird aber nicht behoben, wenn die die Optimierung auf "none" 
umstelle. Dann läuft das Programm ebenfalls nicht.

Was ich mich aber frage - wie dumm ist der GCC denn, dass er aus einem 
Interrupt heraus keine andere Funktion aufrufen kann? das ist ja 
verdammt bitter.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, also ich habe damit noch keine Probleme gehabt (ADUC7026).

Allerdings habe ich die IRQ-Routinen anderst realisiert:
void (*IRQ)(void);
void  IRQ_Routine(void) __attribute__ ((interrupt ("IRQ")));
void IRQ_Routine(void) {
  if (*IRQ !=0x00) {
    IRQ();
  }
}

int main (void)  {
  IRQ = IRQ_Function;  // Weise IRQ Funkion zu
}

void IRQ_Function (void) {
}

Der Startup-Code ist nicht von mir:
vectors:       ldr     PC, Reset_Addr
                ldr     PC, Undef_Addr
                ldr     PC, SWI_Addr
                ldr     PC, PAbt_Addr
                ldr     PC, DAbt_Addr
                nop              /* Reserved Vector */
                ldr     PC, IRQ_Addr
                ldr     PC, FIQ_Addr

Reset_Addr:     .word   Reset_Handler    /* defined in this module below 
*/
Undef_Addr:     .word   UNDEF_Routine    /* defined in irq.c  */
SWI_Addr:       .word   SWI_Routine      /* defined in irq.c  */
PAbt_Addr:      .word   PAbt_Routine    /* defined in irq.c  */
DAbt_Addr:      .word   DAbt_Routine    /* defined in irq.c  */
IRQ_Addr:       .word   IRQ_Routine      /* defined in irq.c  */
FIQ_Addr:       .word   FIQ_Routine      /* defined in irq.c  */



Das ganze ist wegen der eigentlich unnötigen Sprüngen zu Beginn eines 
IRQ nicht umbedingt optimal, allerdings hatte ich bisher weder mit 
Funktionsaufrufen noch mit Optimierungen Probleme.

Allerdings hat mein uC keinen VIC, einige Dinge werden also anderst als 
bei dir sein.

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Tilo
danke, ich versuche es mal auf die Art wie du das machst.
Mal schauen, ob das Abhilfe bringt :-)
Wenns keine Möglichkeit gäbe, aus einer Interruptfunktion eine beliebige 
sonstige Funktion aufzurufen, dann wäre das ziemlich sch..... schade.
Ich muss im Timerinterrupt einen Haufen Sachen erledigen, es wäre 
ziemlich unübersichtlich (und wegen Modularität auch nicht gut) den 
ganzen Code in den Timerinterrupt rein zu packen.

Hat sonst noch niemand das Problem gehabt? :o Bin ich der einzige, der 
im Interrupt eine Funktion aufrufen will?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, bist du nicht. Aber vielleicht der einzige bei dem dieses Problem 
auftritt.

Wobei ich jedoch bei den ARMs einen zentralen Interrupt-Dispatcher 
verwende. Der IRQ landet in einer Assembler-Funktion, die die Register 
sichert, zur Unterstützung von verschachtelten Interrupts den Modus 
wechselt und dann als Handler über denn VIC-Vektor eine normale C 
Funktion aufruft. Die deshalb ggf. auch in Thumb codiert sein kann. Da 
kann das Problem nicht auftreten.

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.:
Kann man sich deinen Startup-Code mit diesen Handlern einmal ansehen?

Bei mir ist einfach eine normale Vector-Tabelle, in dem Stil

vectors:
  b reset
  b undef
  b swi
  b pabort
  b dabort
  nop /* bootloader checksum */
  ldr pc, [pc, #-0x120]
  b fiq


im reset-Handler wird dann für jeden Modus der Stackpointer 
initialisiert, also undef, user, supervisor und so weiter. Nachher kommt 
ein

  b main

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine Variante davon findest du im Startup-Code von 
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm...

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke A.K.
Ich schaue mir das jetz noch ein paar mal an; ich blicke nämlich 
überhaupt nicht durch bei dem Code.
Aber einfach copy & paste möchte ich nicht machen, ich will meinen 
Startup-Code schon selber schreiben, damit ich genau weiss, was abgeht.

Autor: A. K. (prx)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Und das hier ist meine Version. Besonderheit hier ist die Möglichkeit, 
auch ohne Interworking ausschliesslich im Thumb-Mode arbeiten zu können.

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo A.K.,
vielen dank für deinen Code.
Ich habe mal von der Seite, zu welcher du mir den Link angegeben hast, 
ein Beispiel runtergeladen (GPIO mit LPC23xx / LPC24xx).
Da raus habe ich mal testweise den Startup-Code übernommen; und was soll 
ich sagen? Intressanterweise hängt sich der Rechner immer beim 
Initialisieren des VIC auf. Sobald nämlich die ISR installiert werden 
soll, springt er in en dabort-Handler - irgendwie ganz merkwürdig.
Weisst du da was drüber?

Mein Code sieht wie folgt aus:

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

void test(void)
{
  static int a = 0;
  if(a)
    FIO1CLR = 0xFFFFFFFF;
  else
    FIO1SET = 0xFFFFFFFF;
  a = !a;
}

void tick(void)
{
  test();
  T0IR = 0xFF;
  VICAddress = 0;
}


void initmam(void)
{
  MAMTIM = 3;
  MAMCR = 2;
}

void initpll(void)
{
  IRCTRIM = 0xEE;
  PLLCON &= 0xFFFFFFFD;
  PLLFEED = 0xAA;
  PLLFEED = 0x55;
  PLLCON &= 0xFFFFFFFE;
  PLLFEED = 0xAA;
  PLLFEED = 0x55;
  CCLKCFG = 0;
  PLLCFG = 0x000700FC;
  PLLFEED = 0xAA;
  PLLFEED = 0x55;
  PLLCON |= 0x00000001;
  PLLFEED = 0xAA;
  PLLFEED = 0x55;
  CCLKCFG = 3;
  while(!(PLLSTAT & (1 << 26)));
  PLLCON |= 0x00000002;
  PLLFEED = 0xAA;
  PLLFEED = 0x55;b // 72 MHz
}

void initvic(void)
{
  VICIntEnClr = 0xFFFFFFFF;
  VICAddress = 0;
  VICIntSelect = 0;
  VICIntSelect = 0;
}

void inittimer(void)
{
  T0TCR = 0;
  T0PR = 0;
  T0MR0 = 18000;
  T0MCR = 0x03;
  T0TCR = 1;
  VICIntEnable = 0x10;
  VICVectPriority4 = 0;
  VICVectAddr4 = (unsigned long)tick;
}

void initsdram(void)
{
  int i;
  PCONP |= (1 << 11);
  PINSEL5 = 0x55010115;
  PINMODE5 = 0xAA02022A;
  PINSEL6 = 0x55555555;
  PINMODE6 = 0xAAAAAAAA;
  PINSEL7 = 0x55555555;
  PINMODE7 = 0xAAAAAAAA;
  PINSEL8 = 0x15555555;
  PINMODE8 = 0x2AAAAAAA;
  PINSEL9 = (1 << 18);
  PINMODE9 = 0x80000;
  EMCControl = 1;
  EMCDynamicReadConfig = 1;
  EMCDynamicRasCas0 = 0x203;
  EMCDynamictRP = 3;
  EMCDynamictRAS = 5;
  EMCDynamictSREX = 2;
  EMCDynamictAPR = 2;
  EMCDynamictDAL = 5;
  EMCDynamictWR = 3;
  EMCDynamictRC = 6;
  EMCDynamictRFC = 6;
  EMCDynamictXSR = 2;
  EMCDynamictRRD = 3;
  EMCDynamictMRD = 3;
  EMCDynamicConfig0 = ((1 << 14) | (1 << 10) | (1 << 9) | (1 << 7));
  for(i = 0; i < 100; i++);
  EMCDynamicControl = 0x181; // nop
  for(i = 0; i < 100; i++);
  EMCDynamicControl = 0x101; // pall
  EMCDynamicRefresh = 1; // ??
  for(i = 0; i < 100; i++);
  EMCDynamicRefresh = 19;
  EMCDynamicControl = 0x081;
  i = (*(volatile int*)(0xA0000000 | (0x22 << 13))); // 4 burst 2 cas
  EMCDynamicControl = 0;
  EMCDynamicConfig0 |= 0x80000;
}

int main(void)
{
  MEMMAP = 1;
  SCS |= 1;
  initmam();
  initpll();
  initsdram();
  initvic();
  inittimer();
  IO1DIR = FIO1DIR = 0xFFFFFFFF;
  IO1CLR = FIO1CLR = 0xFFFFFFFF;
  while(1)
  {

  }
}

und der Startup-Code sieht so aus:

  .text
  .code 32
  .global reset
  .align 0
  .arm
  .section .vectors, "ax"

start:
  b reset_vector
  b undef_vector
  b swi_vector
  b pabort_vector
  b dabort_vector
  .word 0x99FFFFD8 /* bootloader checksum */
  b irq_vector
  b fiq_vector

  .section .init, "ax"
  .code 32
  .align 0

reset_vector:
  b reset
undef_vector:
  b undef
swi_vector:
  b swi
pabort_vector:
  b pabort
dabort_vector:
  b dabort
irq_vector:
  b irq
fiq_vector:
  b fiq

undef:
  b undef

swi:
  b swi

pabort:
  b pabort

dabort:
  b dabort

irq:
  //ldr pc, [pc, #-0x154]
  sub lr, lr, #4
  stmfd sp!, {lr}
  mrs r14, SPSR
  stmfd sp!, {r14}
  stmfd sp!, {r0}
  ldr r14, =0xFFFFFF00
  ldr r0 , [r14]
  msr CPSR_c, #MODE_SVC
  stmfd sp!, { r1-r3, r12, r14 }
  mov r14, pc
  bx r0
  ldmia sp!, { r1-r3, r12, r14 }
  msr CPSR_c, #I_BIT | MODE_IRQ
  ldmia sp!, {r0}
  ldmia sp!, {r14}
  msr SPSR_cxsf, r14
  ldmia sp!, {pc}^

fiq:
  b fiq

reset:
  ldr r0, =STACK_TOP

  /* switch to UND mode and initialize its stack pointer */
  msr cpsr_c, #MODE_UND | I_BIT | F_BIT
  mov sp, r0
  sub r0, #UND_STACK_SIZE

  /* switch to ABT mode and initialize its stack pointer */
  msr cpsr_c, #MODE_ABT | I_BIT | F_BIT
  mov sp, r0
  sub r0, #ABT_STACK_SIZE

  /* switch to FIQ mode and initialize its stack pointer */
  msr cpsr_c, #MODE_FIQ | I_BIT | F_BIT
  mov sp, r0
  sub r0, #FIQ_STACK_SIZE

  /* switch to IRQ mode and initialize its stack ponter */
  msr cpsr_c, #MODE_IRQ | I_BIT | F_BIT
  mov sp, r0
  sub r0, #IRQ_STACK_SIZE

  /* switch to SVC mode and initialize its stack pointer */
  msr cpsr_c, #MODE_SVC | I_BIT | F_BIT
  mov sp, r0
  sub r0, #SVC_STACK_SIZE

  /* clear all pending interrupts */
  ldr r1, =VICINTENCLR
  mov r2, #0xFFFFFFFF
  str r2, [r1]

  /* clear all pending software interrupts */
  ldr r1, =VICSOFTINTCLR
  mov r2, #0xFFFFFFFF
  str r2, [r1]

  /* switch to USR mode and initialize its stack pointer */
  msr cpsr_c, #MODE_USR /* irq and fiq enabled */
  mov sp, r0
  sub sl, sp, #USR_STACK_SIZE

  ldr r0, =main
  bx r0

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der IRQ-Wrapper dieses Startup-Codes erfordert, dass Interrupt-Handler 
nicht als solche gekennzeichnet werden. Das ist ja u.A. der Sinn 
davon.

Autor: Tilo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wozu muss man überhaupt die IRQ-Funktionen markieren?
Ist dies dafür notwendig, damit der Compiler weiß, welche Register zur 
Verfügung stehen? Die belegten Register werden im Startup-Code von Hand 
freigeräumt, daran kann es also nicht liegen.

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.:

Wow, Klasse! es geht! super Sache :-D
Danke!

Autor: Walter F. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Tilo
> Wozu muss man überhaupt die IRQ-Funktionen markieren?

Damit der Compiler weiss, dass er nun alle Register sichern muss. Und
> damit der Compiler weiß, welche Register zur Verfügung stehen

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und genau bei diesem Prolog/Epilog-Code liegt beim GCC für ARM der Hase 
im Pfeffer. Ich haben mir den mal angesehen und es wundert mich 
eigentlich nur, dass da nicht öfter Mist gebaut wird. Wirkt sehr 
historisch gewachsen und eine Erweiterung stapelt sich über die andere. 
Ich habe den Eindruck, dass sich da keiner so recht heran traut.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tilo schrieb:

> Wozu muss man überhaupt die IRQ-Funktionen markieren?

Ohne einen solchen IRQ-Wrapper im Startup-Code muss man das. Denn dann 
wird der Handler ziemlich direkt aus der Vektorleiste heraus 
angesprungen.

Autor: Tobias Plüss (hubertus)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
A.K,
ich habe da auch mal eine Frage bez¨glich des Startup-Code.
Ich habe hier einiges mitgelesen und das auch auf meinen Code adaptiert, 
funktioniert alles bestens. Das einzige, was ich bisher weggelassen 
hatte, war diese Kopiererei ("Copy initialized data to its execution 
address in RAM") vom Flash ins RAM, weil mir nicht klar war wozu die gut 
sein sollte.
Danach hatte ich einige Probleme mit Variablen, die als static 
deklariert waren. Nach einfügen der Kopierfunktionen hat dann auch das 
geklappt.
Nun habe ich nur noch ein Problem. Und zwar wollte ich einen wirklich 
universellen Startup-Code bauen, der alles nötige initialisiert, sodass 
man auch mit C++ programmieren könnte (wenn man das denn wollte). Also 
muss ich vor dem main-Aufruf noch die C++ Konstruktoren aufrufen, und 
nach dem Beenden von main (was zwar nicht vorkommt, aber der 
vollständigkeit halber) die Destruktoren.
Soweit so gut; doch nachdem ich nun diesen Code noch eingefügt habe, der 
die Konstruktoren aufruft, kommt es in anderen Programmteilen zu 
sporadischen Abstürzen. Es ist reproduzierbar, tritt aber 
interessanterweise nur auf, wenn die Konstruktoren vorher aufgerufen 
wurden; ansonsten nicht. Was ich noch erwähnen sollte ist, dass in 
meinem Testprogramm C und C++ vermischt sind - C++ geht jetzt, dafür C 
nicht mehr ganz richtig. Wenn mehrere Funktionen ineinander 
verschachtelt sind und Rückgabewerte zurückgeben sollen, dann geht das 
nur bis zu einer bestimmten Tiefe, dann stürzt der Rechner ab, ich kann 
mir aber nicht erklären wieso.
Könntest du bitte meinen Code anschauen, und mir sagen, ob es an dem 
liegt? Wär super toll.


Gruss
     Tobias

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.