Forum: Mikrocontroller und Digitale Elektronik HardFault - Grund finden


von M. W. (rallini94)


Lesenswert?

Hallo zusammen,

ich möchte das erste Mal ein Projekt mit einem Cortex M4 machen und bin 
nun das erste Mal bei einem HardFault Fehler gelandet, bei dem ich 
einfach keine Ahnung habe, wie der auftreten kann. Deswegen würde ich 
mich über eure Hilfe freuen.

Wie folgt: Ich habe die zwei Komponenten Display und ADC (Projekt soll 
später mal ein kleines Messgerät werden). Das ganze soll auf einem SAM4S 
Xplaind Pro Board mit einem SAM4SD32C.

Die Main sieht momentan so aus
1
int main(void)
2
{
3
  // Stellt PLL von 12MHz Quarz auf 120 MHz
4
  init_board();
5
6
  // Initalisiert den Systick-Timer als Delay Timer
7
  init_Systick();
8
9
  // ADC Komponente
10
  myADC converter(1000);
11
  converter.DMAtransfer((uint32_t*)converter.ch0_data, 1024);
12
13
14
  //Display Komponente
15
  TFT_Display display;
16
  Graph grafik(&display);
17
  grafik.show_statusleiste(2000000000,10000);
18
  
19
  
20
    // LED auf ewig toggeln lassen
21
  PMC->PMC_PCER0 |= (1<<ID_PIOC);    // Aktivieren des Taktes fuer PORT C
22
  PIOC->PIO_OER  = (1<<23);      // PC23 als ausgang ?
23
    
24
    while (1) 
25
    {
26
    
27
    // Togglt !
28
    PIOC->PIO_SODR |= (1<<23);
29
    Systick_delayms(1000);
30
    PIOC->PIO_CODR |= (1<<23);
31
    Systick_delayms(1000);
32
    
33
    }
34
}

Wenn ich die Zeilen für eine Komponente auskommentiere, läuft alles wie 
es soll. D.h. einzeln laufen die Codes. Sind beide, wie gezeigt 
einkommentiert, so springt der Controller beim Debuggen anstatt in 
"init_Systick();" in den HardFault_Handler(). Also interessanterweise 
bereits vor den Komponenten in einen Fehler.

Woran könnte das liegen?

Danke für eure Hilfe

von Nop (Gast)


Lesenswert?

Setz Dir nen Breakpoint auf Deinen Hardfault-Handler und guck dann an, 
wohin der Stackpointer zeigt. Addier 24 drauf, und Du bekommst die 
Adresse, wo er abgeschmiert ist. Damit gehst Du dann ins Mapfile und 
weißt, in welcher Funktion das war. Oder Du lädst es in den Debugger und 
gehst direkt zu der Adresse, dann siehst Du sogar die Zeile.

In System Control Block bekommst Du noch genauere Auskunft, was 
schiefgegangen ist. Sollte in Registern wie SCB_CFSR und SCB_HFSR oder 
so stehen.

von PittyJ (Gast)


Lesenswert?

Was sind die 1000, 10000 und die 1024? Passen die nicht zusammen?

Und wenn die nicht zusammengehören, wie wäre es mit passenden Konstanten 
und ein paar Kommentaren dazu, warum genau 1000 gewählt wird.

von Mauselicht (Gast)


Lesenswert?

Noch einfacher:

Nimm den Debugger und geh zunächst über die Funktionen drüber, d.h. daß 
die Funktionen ausgeführt werden und der Cursor an der nächsten 
Programmzeile rauskommt ("Step over" oder so). Dann wirst du bald 
merken, wo er sich verabschiedet. Danach machst du einen Restart und 
gehst bis zur nun bekannten Funktion, diesmal aber in die Funktion 
hinein ("Step into") und machst das wie vorher ("Step over") Zeile für 
Zeile. Damit kannst du die Stelle recht schnell eingrenzen. Wenn diese 
dann bekannt ist kannst du nochmal zur Sicherheit einen Durchgang machen 
ob es reproduzierbar ist. Man kann das Steppen abkürzen mit z.B. "Run to 
line" oder so im Debugger.

Aus Erfahrung:
Meistens liegt es an einem kaputten Zeiger bzw. Referenz.

von Nop (Gast)


Lesenswert?

Mauselicht schrieb:
> Noch einfacher:

Geht nicht, wenn das in einem Interrupt oder DMA-Routine abschmiert. 
Deswegen Hardfault-Handler, das geht immer.

von M. W. (rallini94)


Lesenswert?

PittyJ schrieb:
> Was sind die 1000, 10000 und die 1024? Passen die nicht zusammen?
>
> Und wenn die nicht zusammengehören, wie wäre es mit passenden Konstanten
> und ein paar Kommentaren dazu, warum genau 1000 gewählt wird.

1000 ist hier beim initialisieren eine Angabe in Hz, welche Abtastrate 
eingestellt werden soll. Die ist natürlich veränderlich.
1024 ist die Anzahl Daten, die der DMA transportieren soll.
Und die anderen einfach Zahlen zur Ausgabe, die erst später eine 
vernünftige Bedeutung bekommen, sie sind nur da um zu sehen, ob das 
Display sie richtig anzeigt

Mauselicht schrieb:
> Noch einfacher:
>
> Nimm den Debugger und geh zunächst über die Funktionen drüber, d.h. daß
> die Funktionen ausgeführt werden und der Cursor an der nächsten
> Programmzeile rauskommt ("Step over" oder so). Dann wirst du bald
> merken, wo er sich verabschiedet. Danach machst du einen Restart und
> gehst bis zur nun bekannten Funktion, diesmal aber in die Funktion
> hinein ("Step into") und machst das wie vorher ("Step over") Zeile für
> Zeile. Damit kannst du die Stelle recht schnell eingrenzen. Wenn diese
> dann bekannt ist kannst du nochmal zur Sicherheit einen Durchgang machen
> ob es reproduzierbar ist. Man kann das Steppen abkürzen mit z.B. "Run to
> line" oder so im Debugger.
>
> Aus Erfahrung:
> Meistens liegt es an einem kaputten Zeiger bzw. Referenz.

Wie ich oben schrieb: Der Debugger zeigt, dass der HardFault dann 
auftritt, wenn der Controller versucht in die Funktion init_Systick zu 
springen. Also taucht der Fehler schon auf, bevor die neu 
einkommentierten Codezeilen kommen und das verstehe ich nicht. Denn mit 
nur einer Komponente drin, geht die Funktion ja. Ich verstehe halt 
nicht, wie sich dann die zweite Komponente auf diese schon vorher 
abgearbeitete Funktion auswirken kann

Nop schrieb:
> Setz Dir nen Breakpoint auf Deinen Hardfault-Handler und guck dann
> an,
> wohin der Stackpointer zeigt. Addier 24 drauf, und Du bekommst die
> Adresse, wo er abgeschmiert ist. Damit gehst Du dann ins Mapfile und
> weißt, in welcher Funktion das war. Oder Du lädst es in den Debugger und
> gehst direkt zu der Adresse, dann siehst Du sogar die Zeile.
>
> In System Control Block bekommst Du noch genauere Auskunft, was
> schiefgegangen ist. Sollte in Registern wie SCB_CFSR und SCB_HFSR oder
> so stehen.

Werde ich bei Gelegenheit mir anschauen und dann ggf. hier zeigen

von Mauselicht (Gast)


Lesenswert?

Nop schrieb:
> Mauselicht schrieb:
>> Noch einfacher:
>
> Geht nicht, wenn das in einem Interrupt oder DMA-Routine abschmiert.
> Deswegen Hardfault-Handler, das geht immer.
Es war ja auch gesagt, daß es einzeln lauffähig ist. Und noch besser, 
wie nun beschrieben, sogar schon bei der Initialisierung danebengeht.

M. W. schrieb:
>> Aus Erfahrung:
>> Meistens liegt es an einem kaputten Zeiger bzw. Referenz.
>
> Wie ich oben schrieb: Der Debugger zeigt, dass der HardFault dann
> auftritt, wenn der Controller versucht in die Funktion init_Systick zu
> springen.
Wenn das reproduzierbar ist solltest du das mit meiner Methode dort auch 
schnell auffinden.

> Also taucht der Fehler schon auf, bevor die neu
> einkommentierten Codezeilen kommen und das verstehe ich nicht. Denn mit
> nur einer Komponente drin, geht die Funktion ja. Ich verstehe halt
> nicht, wie sich dann die zweite Komponente auf diese schon vorher
> abgearbeitete Funktion auswirken kann
Aber das zeigt doch, daß etwas mit dem Speicher schiefgeht, also z.B. in 
der Instantiierung deiner Funktionen oder eher daß deine Funktionen 
falsch in den Speicher abgebildet werden.

Mach mal so wie ich beschrieben habe. Geht am unkompliziertesten, 
pragmatisch, ohne viel Gefrickel.

Btw: Falls nicht zu groß wäre vielleicht etwas Einblick in deine 
Funktionen hilfreich.

von 640 kByte free! (Gast)


Lesenswert?

Nop schrieb:
> Setz Dir nen Breakpoint auf Deinen Hardfault-Handler und guck dann an,
> wohin der Stackpointer zeigt. Addier 24 drauf, und Du bekommst die
> Adresse, wo er abgeschmiert ist. Damit gehst Du dann ins Mapfile und
> weißt, in welcher Funktion das war. Oder Du lädst es in den Debugger und
> gehst direkt zu der Adresse, dann siehst Du sogar die Zeile.

Ähhm,
wenn du bei einem Hardfault mehr als 'bt' eintippen/anklicken musst, 
hast du deine IDE/Debugger/etc falsch/unvollständig konfiguriert..

von M.K. B. (mkbit)


Lesenswert?

Mauselicht schrieb:
> Nimm den Debugger und geh zunächst über die Funktionen drüber,

Wenn der Controller eine Pipeline in der CPU hat, dann kann es auch 
passieren, dass der HardFault im Debugger auftritt, wenn du schon in 
einer Zeile nach dem kritischen Befehl bist.

von Jim M. (turboj)


Lesenswert?

M. W. schrieb:
> Sind beide, wie gezeigt
> einkommentiert, so springt der Controller beim Debuggen anstatt in
> "init_Systick();" in den HardFault_Handler(). Also interessanterweise
> bereits vor den Komponenten in einen Fehler.

Das Zeuchs sieht wie C++ aus. Könnte das eine große Menge Speicher 
versuchen auf dem Stack anzulegen?

Denn bei einem Stack Overflow in main() funktionieren Funktionsaufrufe 
nicht mehr richtig.

Ein Hinweis wäre auch der Wert im Stack Register, den man innerhalb des 
HardFault Handlers per Debugger ausliest.

von Nop (Gast)


Lesenswert?

640 kByte free! schrieb:

> wenn du bei einem Hardfault mehr als 'bt' eintippen/anklicken musst,
> hast du deine IDE/Debugger/etc falsch/unvollständig konfiguriert..

Denk mal nach: meinst Du, der OP hätte diese Frage dann gepostet? Und 
was hilft dem OP wohl mehr: ihm zu sagen, was er klicken muß, oder ihm 
den Mechanismus dahinter nahezubringen? Den Mechanismus, mit dem er dann 
auch ein Logging oder einen Bluescreen implementieren könnte, wenn die 
IDE nicht am Gerät hängt?

von M. W. (rallini94)


Angehängte Dateien:

Lesenswert?

Jim M. schrieb:
> M. W. schrieb:
>> Sind beide, wie gezeigt
>> einkommentiert, so springt der Controller beim Debuggen anstatt in
>> "init_Systick();" in den HardFault_Handler(). Also interessanterweise
>> bereits vor den Komponenten in einen Fehler.
>
> Das Zeuchs sieht wie C++ aus. Könnte das eine große Menge Speicher
> versuchen auf dem Stack anzulegen?
>
> Denn bei einem Stack Overflow in main() funktionieren Funktionsaufrufe
> nicht mehr richtig.
>
> Ein Hinweis wäre auch der Wert im Stack Register, den man innerhalb des
> HardFault Handlers per Debugger ausliest.

Ja, soll C++ werden. Zugegebenermaßen war ich bis jetzt auch eher mit C 
und halt kleineren Controllern unterwegs. Hier wollte ich mal die 
Objektorientierung von C++ mit ausnutzen. Das ganze übersteigt aber 
scheinbar gerade noch meine Fähigkeiten.

Ich kann mir gut vorstellen, dass der Stack zu viel aufgedrückt bekommen 
hat. Jetzt dachte ich, ich kann die Objekte ja eigentlich auch außerhalb 
von main anlegen und dann nur dort initialisieren und hab es nun wie 
folgt umgeschrieben
1
// Variablen Global definieren
2
myADC *converter;
3
TFT_Display *display;
4
Graph *grafik;
5
6
int main(void)
7
{
8
    /* Initialize the SAM system */
9
    //SystemInit();
10
  init_board();
11
  init_Systick();
12
13
  
14
  //myADC converter(1000);
15
  converter = new myADC(1000);
16
  converter.DMAtransfer((uint32_t*)converter.ch0_data, 1024);
17
  converter.DMAtransfer((uint32_t*)converter.ch0_data, 1024);
18
  
19
  display = new TFT_Display();
20
  grafik = new Graph(display);
21
  grafik->show_statusleiste(2000000000,10000);
22
  
23
  
24
    /* Replace with your application code */
25
  PMC->PMC_PCER0 |= (1<<ID_PIOC);    // Aktivieren des Taktes fuer PORT C
26
  PIOC->PIO_OER  = (1<<23);      // PC23 als ausgang ?
27
    
28
    while (1) 
29
    {
30
    
31
    // Togglt !
32
    PIOC->PIO_SODR |= (1<<23);
33
    Systick_delayms(1000);
34
    PIOC->PIO_CODR |= (1<<23);
35
    Systick_delayms(1000);
36
    
37
    }
38
}

Leider führt das zu den neuen Fehlern im Bild "Fehlerliste.png". Die 
dazu gefunden google Sucheinträge helfen mir leider nicht weiter bzw 
verstehe ich sie nicht ganz. Kann mir jemand die Fehler erklären und 
sagen, wie sie weg gehen?

Danke für eure Hilfe für einen Neuling im Bereich Cortex-M/größerer 
Controller

von c-hater (Gast)


Lesenswert?

M. W. schrieb:

> Danke für eure Hilfe für einen Neuling im Bereich Cortex-M/größerer
> Controller

Das hat mit Cortex-M/größerer Controller rein garnix zu tun. Nur damit, 
dass du nichtmal deine Werkzeuge beherrschst. Das läßt Schlimmes für die 
Sprache ahnen...

von Mauselicht (Gast)


Lesenswert?

Kenne ich so ähnlich mit "undefined reference to '__sbrk'". Hatte ich in 
C auch schon. Das kam von einer Benutzung von printf() oder einem 
Derivat davon. Sicherlich in deiner Displayfunktion. Der Linker findet 
die dazu notwendige binäre Lib nicht. Die Lösung bestand darin, eine 
Quelldatei einer Minilib mit einem eigenen printf() oder miniprintf() 
oder so ähnlich einzubinden. Bei dir sind aber auch mehrere Funktionen 
in Benutzung, die der "ld" nicht findet. Prüfe erstmal die Pfade des 
Linkers und ob du überhaupt eine entsprechende Lib oder Libc an Bord 
hast oder kommentiere erstmal die Lib-Aufrufe raus.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Es ist auch eine ganz nette Fingerübung den HardFault Handler selber zu 
füllen mit einem Registerdump.
(Zusätzlich auch die anderen zu aktivieren und zu nutzen)
Im System Control Block gibts zudem noch Register die genauer 
beschreiben warums geknallt hat.

Hier schön beschrieben:
www.keil.com/appnotes/files/apnt209.pdf

von M. W. (rallini94)


Lesenswert?

c-hater schrieb:
> M. W. schrieb:
>
>> Danke für eure Hilfe für einen Neuling im Bereich Cortex-M/größerer
>> Controller
>
> Das hat mit Cortex-M/größerer Controller rein garnix zu tun. Nur damit,
> dass du nichtmal deine Werkzeuge beherrschst. Das läßt Schlimmes für die
> Sprache ahnen...

ja, dsa mag sein. Ich bin eher der Hardware-Mensch und bis jetzt sind 
mir solche Fehler halt noch nicht aufgetreten und deswegen habe ich auch 
kein wissen darüber

Mauselicht schrieb:
> Kenne ich so ähnlich mit "undefined reference to '__sbrk'". Hatte
> ich in
> C auch schon. Das kam von einer Benutzung von printf() oder einem
> Derivat davon. Sicherlich in deiner Displayfunktion. Der Linker findet
> die dazu notwendige binäre Lib nicht. Die Lösung bestand darin, eine
> Quelldatei einer Minilib mit einem eigenen printf() oder miniprintf()
> oder so ähnlich einzubinden. Bei dir sind aber auch mehrere Funktionen
> in Benutzung, die der "ld" nicht findet. Prüfe erstmal die Pfade des
> Linkers und ob du überhaupt eine entsprechende Lib oder Libc an Bord
> hast oder kommentiere erstmal die Lib-Aufrufe raus.

printf() oder ähnliches nutze ich gar nicht.

Wie prüfe ich denn die Pfade des Linker in Atmel Studio 7 und was müsste 
ich da finden? ich kenne mich damit tatsäcglich gar nicht aus. Bei 
meinen kleineren Projekten bis jetzt musste ich mich darum immer nicht 
kümmern, da haben die Standardeinstellungen ausgereicht

von M. W. (rallini94)


Lesenswert?

Oh es geht jetzt!!!

Ich habe bei den ganzen Projekt einstellungen Unter ARM/GNU Linker -> 
General -> Additional Specs auf "Use Syscall Stubs 
(--specs=nosys.specs)" gestellt. Dann kam nur noch ein "undefined 
reference to end"

nach 
http://ww1.microchip.com/downloads/en/DeviceDoc/Frequently-Asked-Questions-4.9.3.26.txt 
scheint das noch ein Problem mit der Speicherbereichszuweisung zu sein. 
Aus dem Linker Skript habe ich dann vor dem end den _ entfernt und es 
ging
1
/* ---------------------------------------------------------------------------- */
2
/*                  Atmel Microcontroller Software Support                      */
3
/*                       SAM Software Package License                           */
4
/* ---------------------------------------------------------------------------- */
5
/* Copyright (c) %copyright_year%, Atmel Corporation                                        */
6
/*                                                                              */
7
/* All rights reserved.                                                         */
8
/*                                                                              */
9
/* Redistribution and use in source and binary forms, with or without           */
10
/* modification, are permitted provided that the following condition is met:    */
11
/*                                                                              */
12
/* - Redistributions of source code must retain the above copyright notice,     */
13
/* this list of conditions and the disclaimer below.                            */
14
/*                                                                              */
15
/* Atmel's name may not be used to endorse or promote products derived from     */
16
/* this software without specific prior written permission.                     */
17
/*                                                                              */
18
/* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
19
/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
20
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
21
/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
22
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
23
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
24
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
25
/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
26
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
27
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
28
/* ---------------------------------------------------------------------------- */
29
30
/* Section Definitions */
31
SECTIONS
32
{
33
    .text :
34
    {
35
        . = ALIGN(4);
36
        _sfixed = .;
37
        KEEP(*(.vectors .vectors.*))
38
        *(.text .text.* .gnu.linkonce.t.*)
39
        *(.glue_7t) *(.glue_7)
40
        *(.rodata .rodata* .gnu.linkonce.r.*)
41
        *(.ARM.extab* .gnu.linkonce.armextab.*)
42
43
        /* Support C constructors, and C destructors in both user code
44
           and the C library. This also provides support for C++ code. */
45
        . = ALIGN(4);
46
        KEEP(*(.init))
47
        . = ALIGN(4);
48
        __preinit_array_start = .;
49
        KEEP (*(.preinit_array))
50
        __preinit_array_end = .;
51
52
        . = ALIGN(4);
53
        __init_array_start = .;
54
        KEEP (*(SORT(.init_array.*)))
55
        KEEP (*(.init_array))
56
        __init_array_end = .;
57
58
        . = ALIGN(0x4);
59
        KEEP (*crtbegin.o(.ctors))
60
        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
61
        KEEP (*(SORT(.ctors.*)))
62
        KEEP (*crtend.o(.ctors))
63
64
        . = ALIGN(4);
65
        KEEP(*(.fini))
66
67
        . = ALIGN(4);
68
        __fini_array_start = .;
69
        KEEP (*(.fini_array))
70
        KEEP (*(SORT(.fini_array.*)))
71
        __fini_array_end = .;
72
73
        KEEP (*crtbegin.o(.dtors))
74
        KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
75
        KEEP (*(SORT(.dtors.*)))
76
        KEEP (*crtend.o(.dtors))
77
78
        . = ALIGN(4);
79
        _efixed = .;            /* End of text section */
80
    } > rom
81
82
    /* .ARM.exidx is sorted, so has to go in its own output section.  */
83
    PROVIDE_HIDDEN (__exidx_start = .);
84
    .ARM.exidx :
85
    {
86
      *(.ARM.exidx* .gnu.linkonce.armexidx.*)
87
    } > rom
88
    PROVIDE_HIDDEN (__exidx_end = .);
89
90
    . = ALIGN(4);
91
    _etext = .;
92
93
    .relocate : AT (_etext)
94
    {
95
        . = ALIGN(4);
96
        _srelocate = .;
97
        *(.ramfunc .ramfunc.*);
98
        *(.data .data.*);
99
        . = ALIGN(4);
100
        _erelocate = .;
101
    } > ram
102
103
    /* .bss section which is used for uninitialized data */
104
    .bss (NOLOAD) :
105
    {
106
        . = ALIGN(4);
107
        _sbss = . ;
108
        _szero = .;
109
        *(.bss .bss.*)
110
        *(COMMON)
111
        . = ALIGN(4);
112
        _ebss = . ;
113
        _ezero = .;
114
    } > ram
115
116
    /* stack section */
117
    .stack (NOLOAD):
118
    {
119
        . = ALIGN(8);
120
        _sstack = .;
121
        . = . + STACK_SIZE;
122
        . = ALIGN(8);
123
        _estack = .;
124
    } > ram
125
126
    /* heap section */
127
    .heap (NOLOAD):
128
    {
129
        . = ALIGN(8);
130
         _sheap = .;
131
        . = . + HEAP_SIZE;
132
        . = ALIGN(8);
133
        _eheap = .;
134
    } > ram
135
136
    . = ALIGN(4);
137
    end = . ;                   // hier vor dem end den _ entfernt
138
    _ram_end_ = ORIGIN(ram) + LENGTH(ram) -1 ;
139
}

Habe ich damit nun neue Problem zu befürchten oder kann ich das erstmal 
so stehen lassen?

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.