Forum: Mikrocontroller und Digitale Elektronik Interruptroutine verlassen AT91SAM7A3, Eclipse, GNU


von Tobias (Gast)


Lesenswert?

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
von Clemens L. (c_l)


Lesenswert?

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?

von Tobias M. (tobias_m327)


Lesenswert?

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 
:-/).

von Tobias M. (tobias_m327)


Lesenswert?

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

von Jim M. (turboj)


Lesenswert?

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.

von Tobias (Gast)


Lesenswert?

@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 :-/

von W.S. (Gast)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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
von Tobias (Gast)


Lesenswert?

@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

von Marco H. (damarco)


Lesenswert?

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