Liebe Community, ich habe ein Problem (so fangen wohl die meisten Forumbeiträge an): Seit einiger Zeit habe ich meinen Mikrocontroller und die Umgebung einigermaßen im Griff - erfolgreich PWM Signale erzeugt, LCD angesteuert und LEDs blinken lassen (gesteuert über Warteschleifen). Jetzt benötige ich zum Einlesen von PWM Signalen Interruptroutinen. Das klappt auch soweit schon mal, allerdings bricht das Programm nach Abarbeiten der Interruptroutine ab und kehrt nicht ins Hauptprogramm zurück. Tüftel nun seit vielen Wochen an dem Problem rum und bekomm es einfach nicht hin. Kann mir jemand einen Tipp geben, woran es mit eröhter Wahrscheinlichkeit liegen könnte? - Makefile - Irgendwelche Speicherinitialisierung Ich muss dazu sagen, dass ich die Entwicklungsumgebung nicht komplett durchblicke, habe sie aber eigenständig zum Laufen gebracht... auch viel in den Datenblättern gelesen, aber bekomme das Problem nicht in den Griff :-( SEUFZ Habe ein AT91SAM7A3 Entwicklerboard, benutze Eclipse, USBProg und ein vorgefertigtes Makefile, das bisher immer funktioniert hat. Gerne lade ich auch Files und Code hoch, wollte euch nur im ersten Schritt nicht zu-spammen, weil ich ja keine Ahnung habe, in welcher Ecke das Problem stecken könnte. Vielen Dank im Voraus für alle Tips und Tricks! Gruß Tobias
:
Verschoben durch Moderator
Tobias schrieb: > allerdings bricht das Programm nach Abarbeiten der Interruptroutine ab Was genau meinst du damit? Dass überhaupt nichts mehr ausgeführt wird? > und kehrt nicht ins Hauptprogramm zurück. Wurde das Hauptprogramm denn vorher überhaupt ausgeführt, oder war die CPU gerade schlafen?
Habe das Programm gerade nochmals ausführen lassen: In meiner main-Routine habe ich ein zeitgesteuertes Blinkmuster in einer Endlosschleife. In der Interrupt-Routine (ausgelöst durch ein High-Signal an einem Pin) habe ich ein anderes Blink-Muster. Sobald ich also den besagten Pin auf high setze, läuft die Interrupt-Routine. Und zwar dauernd wiederholt. Bis vor ein paar Tagen blieb das Programm noch stehen, wenn ich den Pin wieder auf low gesetzt habe, nun läuft die Interrupt-Routine dauernd weiter (und ich weiß leider nicht mehr, an was ich alles rumgedreht habe :-/).
Hier kommen noch drei Code-Schnipsel, die einen Hinweis auf das Problem enthalten könnten: 1. den Interrupt Handler:
1 | static void interrupt_handler_PB18 (void) |
2 | {
|
3 | AT91C_BASE_AIC->AIC_IDCR = (1<<3); //disable interrupt 3 (=PIO B) |
4 | AT91C_BASE_AIC->AIC_ICCR = (1<<3); //Interrupt Clear Command Register |
5 | blink(10,100); |
6 | blink(1,2000); |
7 | while((AT91C_BASE_PIOB->PIO_PDSR & (1<<18)));//Wait for the PIOB 18 to be 0 (taster loslassen). |
8 | blink(1,5000); |
9 | |
10 | blink(1,5000); |
11 | AT91C_BASE_AIC->AIC_IVR = 0x0; |
12 | blink(1,5000); |
13 | AT91C_BASE_PIOA -> PIO_CODR = (LED_C | LED_D); |
14 | AT91C_BASE_PIOA -> PIO_SODR = (LED_B ); |
15 | blink(20,100); |
16 | AT91C_BASE_AIC->AIC_IECR = (1<<3); //enable interrupt 3 |
17 | blink(1,5000); |
18 | AT91C_BASE_AIC->AIC_EOICR = 0x0;//End of interrupt command register |
19 | }
|
2. cstartup.S (wobei ich den Code 1:1 von Atmel übernommen habe)
1 | #include "project.h" |
2 | |
3 | #define TOP_OF_MEMORY (AT91C_ISRAM + AT91C_ISRAM_SIZE)
|
4 | #define ABT_STACK_SIZE 8*3*4
|
5 | #define IRQ_STACK_SIZE 8*3*4
|
6 | |
7 | #define ARM_MODE_ABT 0x17
|
8 | #define ARM_MODE_FIQ 0x11
|
9 | #define ARM_MODE_IRQ 0x12
|
10 | #define ARM_MODE_SVC 0x13
|
11 | |
12 | #define I_BIT 0x80
|
13 | #define F_BIT 0x40
|
14 | |
15 | |
16 | /* Application startup entry point */
|
17 | .globl reset_handler |
18 | .align 4 |
19 | |
20 | .section .vectors |
21 | .arm |
22 | |
23 | |
24 | /* Exception vectors (should be a branch to be detected as a valid code by the rom */
|
25 | _exception_vectors: |
26 | reset_vector: |
27 | ldr pc, =reset_handler |
28 | undef_vector: |
29 | b undef_vector /* Undefined Instruction */ |
30 | swi_vector: |
31 | b swi_vector /* Software Interrupt */ |
32 | pabt_vector: |
33 | ldr pc, =pabt_handler /* Prefetch Abort */ |
34 | dabt_vector: |
35 | ldr pc, =dabt_handler /* Data Abort */ |
36 | rsvd_vector: |
37 | b rsvd_vector /* reserved */ |
38 | irq_vector: |
39 | b irq_handler /* IRQ : read the AIC */ |
40 | fiq_vector: |
41 | b fiq_handler |
42 | /*------------------------------------------------------------------------------
|
43 | *- Function : fiq_handler
|
44 | *- Treatments : FIQ Interrupt Handler.
|
45 | *- Called Functions :
|
46 | *------------------------------------------------------------------------------*/
|
47 | fiq_handler: |
48 | b fiq_handler |
49 | |
50 | /*------------------------------------------------------------------------------
|
51 | *- Function : irq_handler
|
52 | *- Treatments : IRQ Controller Interrupt Handler.
|
53 | *- Called Functions : AIC_IVR[interrupt]
|
54 | *------------------------------------------------------------------------------*/
|
55 | irq_handler: |
56 | /* Save interrupt context on the stack to allow nesting */
|
57 | sub lr, lr, #4 |
58 | stmfd sp!, {lr} |
59 | mrs lr, SPSR |
60 | stmfd sp!, {r0, lr} |
61 | |
62 | /* Write in the IVR to support Protect Mode */
|
63 | ldr lr, =AT91C_BASE_AIC |
64 | ldr r0, [r14, #AIC_IVR] |
65 | str lr, [r14, #AIC_IVR] |
66 | |
67 | /* Branch to interrupt handler in Supervisor mode */
|
68 | msr CPSR_c, #ARM_MODE_SVC |
69 | stmfd sp!, {r1-r3, r4, r12, lr} |
70 | mov lr, pc |
71 | bx r0 |
72 | ldmia sp!, {r1-r3, r4, r12, lr} |
73 | msr CPSR_c, #ARM_MODE_IRQ | I_BIT |
74 | |
75 | /* Acknowledge interrupt */
|
76 | ldr lr, =AT91C_BASE_AIC |
77 | str lr, [r14, #AIC_EOICR] |
78 | |
79 | /* Restore interrupt context and branch back to calling code */
|
80 | ldmia sp!, {r0, lr} |
81 | msr SPSR_cxsf, lr |
82 | ldmia sp!, {pc}^ |
83 | |
84 | |
85 | /*------------------------------------------------------------------------------
|
86 | *- Function : reset_handler
|
87 | *- Treatments : Reset Interrupt Handler.
|
88 | *- Called Functions : lowlevel_init
|
89 | * main
|
90 | *------------------------------------------------------------------------------*/
|
91 | .section .text |
92 | reset_handler: |
93 | ldr pc, =_low_level_init |
94 | |
95 | /*------------------------------------------------------------------------------
|
96 | *- Low level Init is performed in a C function: lowlevel_init
|
97 | *- Init Stack Pointer to a valid memory area before calling lowlevel_init
|
98 | *------------------------------------------------------------------------------*/
|
99 | /*- Temporary stack in internal RAM for Low Level Init execution */
|
100 | _low_level_init: |
101 | ldr r2, =_lp_ll_init |
102 | ldmia r2, {r0, r1} |
103 | mov sp, r1 |
104 | mov lr, pc |
105 | bx r0 /* Branch on C function (interworking) */ |
106 | |
107 | /*------------------------------------------------------------------------------
|
108 | *- Remap SRAM at 0x0
|
109 | *------------------------------------------------------------------------------*/
|
110 | _remap: |
111 | ldr r2, _lp_remap |
112 | mov r0, #AT91C_MC_RCB |
113 | str r0, [r2] |
114 | |
115 | /*------------------------------------------------------------------------------
|
116 | *- Setup the stack for each mode
|
117 | *------------------------------------------------------------------------------*/
|
118 | _stack_init: |
119 | ldr r2, =_lp_stack_init |
120 | ldmia r2, {r0, r1, r2} |
121 | |
122 | /*- Set up Abort Mode and set ABT Mode Stack */
|
123 | msr CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT |
124 | mov sp, r0 |
125 | sub r0, r0, r1 |
126 | |
127 | /*- Set up Interrupt Mode and set IRQ Mode Stack */
|
128 | msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT |
129 | mov sp, r0 |
130 | sub r0, r0, r2 |
131 | |
132 | /*- Enable interrupt & Set up Supervisor Mode and set Supervisor Mode Stack */
|
133 | msr CPSR_c, #ARM_MODE_SVC | F_BIT |
134 | mov sp, r0 |
135 | |
136 | /*------------------------------------------------------------------------------
|
137 | *- Segments initialization
|
138 | *------------------------------------------------------------------------------*/
|
139 | /* Copy the data section in RAM at 0x0000 */
|
140 | _init_data: |
141 | ldr r2, =_lp_data |
142 | ldmia r2, {r1, r3, r4} |
143 | 1: |
144 | cmp r3, r4 |
145 | ldrcc r2, [r1], #4 |
146 | strcc r2, [r3], #4 |
147 | bcc 1b |
148 | |
149 | /* Clear the bss segment */
|
150 | _init_bss: |
151 | ldr r2, =_lp_bss |
152 | ldmia r2, {r3, r4} |
153 | mov r2, #0 |
154 | 1: |
155 | cmp r3, r4 |
156 | strcc r2, [r3], #4 |
157 | bcc 1b |
158 | |
159 | /*------------------------------------------------------------------------------
|
160 | *- Branch to the main
|
161 | *------------------------------------------------------------------------------*/
|
162 | _branch_main: |
163 | ldr r0, =main |
164 | mov lr, pc |
165 | bx r0 |
166 | |
167 | /*------------------------------------------------------------------------------
|
168 | *- Litteral pools
|
169 | *------------------------------------------------------------------------------*/
|
170 | _lp_ll_init: |
171 | .word lowlevel_init |
172 | .word TOP_OF_MEMORY /* Default SVC stack after power up */ |
173 | |
174 | _lp_remap: |
175 | .word AT91C_MC_RCR |
176 | |
177 | _lp_stack_init: |
178 | .word TOP_OF_MEMORY /* Top of the stack */ |
179 | .word ABT_STACK_SIZE /* ABT stack size */ |
180 | .word IRQ_STACK_SIZE /* IRQ stack size */ |
181 | |
182 | _lp_bss: |
183 | .word _sbss |
184 | .word _ebss |
185 | |
186 | _lp_data: |
187 | .word _etext |
188 | .word _sdata |
189 | .word _edata |
3. Mein Makefile (davon habe ich ehrlich gesagt keine Ahnung - allerdings hat die Compiliererei bisher geklappt :) )
1 | ifndef ERASE_FCT |
2 | #ERASE_FCT=del /F
|
3 | ERASE_FCT=rm |
4 | endif
|
5 | ifndef CROSS_COMPILE |
6 | CROSS_COMPILE=arm-elf- |
7 | endif
|
8 | |
9 | TOOLCHAIN=gcc |
10 | |
11 | OUTFILE_SRAM=at91sam7a3_getting_started_sram |
12 | OUTFILE_FLASH=at91sam7a3_getting_started_flash_neu |
13 | |
14 | # Name of output binary file
|
15 | TARGET=AT91SAM7A3 |
16 | |
17 | INCL=./include |
18 | |
19 | # Comment the line below for debug mode
|
20 | #OPTIM=-Os
|
21 | |
22 | ifeq ($(TOOLCHAIN), gcc) |
23 | |
24 | AS=$(CROSS_COMPILE)gcc |
25 | CC=$(CROSS_COMPILE)gcc |
26 | LD=$(CROSS_COMPILE)gcc |
27 | NM= $(CROSS_COMPILE)nm |
28 | SIZE=$(CROSS_COMPILE)size |
29 | OBJCOPY=$(CROSS_COMPILE)objcopy |
30 | OBJDUMP=$(CROSS_COMPILE)objdump |
31 | CCFLAGS=-g -mcpu=arm7tdmi $(OPTIM) -Wall -I$(INCL) -D$(TARGET) |
32 | ASFLAGS=-D__ASSEMBLY__ -D$(TARGET) -g -mcpu=arm7tdmi -c $(OPTIM) -Wall -I$(INCL) |
33 | |
34 | # Linker flags.
|
35 | # -Wl,...: tell GCC to pass this to linker.
|
36 | # -Map: create map file
|
37 | # --cref: add cross reference to map file
|
38 | # -lc : tells the linker to tie in newlib
|
39 | # -lgcc : tells the linker to tie in newlib
|
40 | LDFLAGS+=-nostartfiles -Wl,--cref |
41 | LDFLAGS+=-lc -lgcc |
42 | LDFLAGS+=-T elf32-littlearm.lds |
43 | OBJS=cstartup.o |
44 | |
45 | endif
|
46 | |
47 | OBJS+= lowlevel.o \ |
48 | main.o |
49 | |
50 | rebuild: clean all |
51 | |
52 | all: sram flash |
53 | |
54 | sram: $(OBJS) |
55 | $(LD) $(LDFLAGS) -Ttext 0x201000 -Tdata 0x200000 -n -o $(OUTFILE_SRAM).elf $(OBJS) |
56 | $(OBJCOPY) --strip-debug --strip-unneeded $(OUTFILE_SRAM).elf -O binary $(OUTFILE_SRAM).bin |
57 | |
58 | flash: $(OBJS) |
59 | $(LD) $(LDFLAGS) -Ttext 0x100000 -Tdata 0x200000 -n -o $(OUTFILE_FLASH).elf $(OBJS) |
60 | $(OBJCOPY) --strip-debug --strip-unneeded $(OUTFILE_FLASH).elf -O binary $(OUTFILE_FLASH).bin |
61 | |
62 | main.o: main.c |
63 | $(CC) -c $(CCFLAGS) main.c -o main.o |
64 | |
65 | lowlevel.o: lowlevel.c |
66 | $(CC) -c $(CCFLAGS) lowlevel.c -o lowlevel.o |
67 | |
68 | cstartup.o: cstartup.S |
69 | $(AS) $(ASFLAGS) cstartup.S -o cstartup.o |
70 | |
71 | clean: |
72 | $(ERASE_FCT) *.o *.bin *.elf |
Tobias M. schrieb: > Bis vor ein paar Tagen blieb das Programm noch stehen, wenn ich den Pin > wieder auf low gesetzt habe, nun läuft die Interrupt-Routine dauernd > weiter (und ich weiß leider nicht mehr, an was ich alles rumgedreht habe > :-/). Das ist meistens ein Hinweis darauf, das der Interrupt Handler das auslösende Bit im Peripherial nicht zurück setzt (hier wohl GPIO oder externer Interrupt). Dadurch wird der Handler beim Verlassen sofort erneut aufgerufen.
@Jim Meba: Danke für deinen Hinweis. Habe einen extern getriggerten Interrupt (wenn Pin auf high gesetzt wird). Ich werde heute Abend nochmal im Datenblatt unter Peripherals nachblättern. Kannst du noch etwas spezifischerres zum Zurücksetzen des auslösenden Bits sagen? Das könnte evtl. meine Blätterdauer gewaltig reduzieren - für mich als Voll-Anfänger :-/
Tobias schrieb: > Kann mir jemand einen Tipp geben, woran es mit eröhter > Wahrscheinlichkeit liegen könnte? > - Makefile > - Irgendwelche Speicherinitialisierung > Ich muss dazu sagen, dass ich die Entwicklungsumgebung nicht komplett > durchblicke Also, da gibt es eine ganze Reihe von möglichen Fehlerursachen, insbesondere weil es sich um einen ARM7TDMI handelt: 1. der Interrupt-Stack ist falsch oder garnicht aufgesetzt. Ort: Startupcode. Behebung: Lernbetty runterladen, den dortigen Startupcode verstehend lesen oder an deinen µC angepaßt verwenden. Bedenke, daß der ARM7TDMI mehrere verschiedene Modi nebst getrennten Stacks UND separate Stackpointer kennt. 2. dein Gnu-Compiler ist faul, sowas gab's schon mal. Abhilfe: die Bastlerversion vom Keil runterladen und damit erstmal soweit zu Potte kommen, bis daß du firm genug bist. Dann kannst du immer noch zum Gnu-Compiler wechseln. 3. falscher Modus überhaupt. Der GCC kennt keine Pragmas, um Interrupthandler (die im ARM-Modus ausgeführt werden müssen) entsprechend zu kennzeichnen. Hat Bedeutung, wenn du den Rest im THUMB-Modus compilieren läßt. 4. (siehe deine Quelle) du hast vergessen, die ISR als solche kenntlich zu machen. Also das GCC-Äquivalent von __irq in die Kopfzeile des Handlers reinschreiben. (den genauen Wortlaut merke ICH nir gewiß nicht, also auch dazu ein Blick in die Lernbetty, dort ist das ganze nämlich auch vorgeturnt) W.S.
Tobias schrieb: > Kannst du noch etwas spezifischerres zum Zurücksetzen des auslösenden > Bits sagen? Das könnte evtl. meine Blätterdauer gewaltig reduzieren - Schau mal in der Registerbeschreibung des betreffenden Peripheriegerätes das den Interrupt auslöst (jedes Peripheriegerät sollte sein eigenes Kapitel haben und in jedem findest Du einen Abschnitt mit der kompletten Beschreibung all der Register dieses Peripheriegerätes mit jedem einzelnen Bit). Dann schau mal ob Du sowas wie ein Statusregister siehst oder eventuell auch nur ein paar verstreute Bits in anderen Registern die "Flag" oder den Buchstaben "F" an verdächtiger Stelle im Namen tragen, lies die Beschreibung dieses Bits. Meist steht dann dort sowas wie "Das Bit wird gesetzt wenn dieser oder jener Zustand aufgetreten ist, ist es wird zurückgesetzt indem eine 1 in das Bit geschrieben wird" oder auch je nach Gerät "es wird zurückgesetzt wenn das Datenregister gelesen wird" oder ähnliches. Die Interrupts die das Peripheriegerät auslösen kann sind fast immer an solche Flags gekoppelt, wenn der zugehörige Interrupt enabled ist und das Flag gesetzt wird löst der Interrupt aus und dann musst Du das tun was nötig ist im dieses Flag wieder zu löschen. Wenn das Gerät mehrere verschiedene Interrupts auslösen kann die alle in der selben Interruptroutine münden dann fragst Du dort zunächst ab welches der in Frage kommenden Flags gesetzt ist (dann weißt Du was die Ursache war, z,B. Welcher Pin es war der den PinChange ausgelöst hat, oder bei anderen Geräten ob es ankommende Daten, ein Fehler, ein Überlauf, oder was auch immer war, jedes davon hat sein eigenes Flag und jedes will auf eine ganz bestimmte Weise zurückgesetzt werden, wie das genau geht steht für jedes Flag in seiner jeweiligen Beschreibung) > Das könnte evtl. meine Blätterdauer gewaltig reduzieren Du wirst nicht umhinkommen das in ein paar ruhigen Stunden (Tagen, Wochen) mal ausgiebig zu studieren, daran führt mittelfristig kein Weg vorbei.
:
Bearbeitet durch User
@Bernd und W.S.: 1000 Dank für die Mühe, die ihr euch gemacht habt! Damit werde ich weiter kommen. Lernbetty zieh ich mir auf jeden fall mal rein :) Ich hoffe noch, dass es mit dem von Bernd beschriebenen Thema zusammenhängt. Die Schritte von W.S. übersteigen mindestens teilweise meinen Horizont. Ich melde mich auf jeden Fall, ob ich's hinkrieg, bzw. falls nicht frag ich sicher nochmal nach :) Selbstverständlich nicht vor Lernbetty und ausgiebigem Testen ;) Viele Grüße und Danke nochmal Tobias
Das Problem ist das die ARMs Tail-chaining unterstützen. Heißt tritt ein weiterer oder auch der selbe Interrupt auf wird der hinten dran gehängt. Von einer höheren Priorität kann dieser auch unterbrochen werden. Flags von PIOs müssen meist zurückgesetzt werden in dem man in ein entsprechenden Register schreibt. Macht man das nicht wird die Routine wieder aufgerufen :) So lange bis sie von einen höheren unterbrochen wird. Oder in das clear Register geschrieben wurde. Am Ende einer Interrupt Routine muss also das clear Register beschrieben werden oder das Interrupt Register am Anfang ausgelesen werden womit es zurückgesetzt wird. Was wo wie nötig ist steht in der Referenz. Das dass Vector table nicht stimmt ist unwahrscheinlich da der Pointer von der Routine ja gefunden wird.
:
Bearbeitet durch User
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.