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?
Ist es so schwer die Fehlermeldung in Google einzugeben ?? http://ccgi.rowley.co.uk/support/faq.php?do=article&articleid=121
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.
Hm, also ich habe damit noch keine Probleme gehabt (ADUC7026). Allerdings habe ich die IRQ-Routinen anderst realisiert:
1 | void (*IRQ)(void); |
2 | void IRQ_Routine(void) __attribute__ ((interrupt ("IRQ"))); |
3 | void IRQ_Routine(void) { |
4 | if (*IRQ !=0x00) { |
5 | IRQ(); |
6 | }
|
7 | }
|
8 | |
9 | int main (void) { |
10 | IRQ = IRQ_Function; // Weise IRQ Funkion zu |
11 | }
|
12 | |
13 | void IRQ_Function (void) { |
14 | }
|
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.
@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?
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.
@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
Eine Variante davon findest du im Startup-Code von http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/#lpc23xx_demo1
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.
Und das hier ist meine Version. Besonderheit hier ist die Möglichkeit, auch ohne Interworking ausschliesslich im Thumb-Mode arbeiten zu können.
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
Der IRQ-Wrapper dieses Startup-Codes erfordert, dass Interrupt-Handler nicht als solche gekennzeichnet werden. Das ist ja u.A. der Sinn davon.
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.
@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
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.
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.
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
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.