Forum: Compiler & IDEs [STM32/CLion] snprintf verursacht HardFault


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich reihe mich in die Schlange der Personen ein, bei denen der Aufruf 
von s(n)printf bei Umwanldung einer beliebigen Ganzzahl (kein float) in 
einem HardFault mündet. Leider kann ich den Grund dafür nicht finden.

Soll heißen, wenn ich den Code Step-by-Step mit dem Debugger durchgehe, 
komme ich bis zum Aufruf von snprintf(). Danach hängt alles, bis ich den 
Debugger interrupte, woraufhin ich mich in der HardFault Schleife 
wiederfinde. Ein schrittweise Step-In in die snprinft() Funktion klappt 
ebenfalls nicht, da ich sofort in oben beschriebener Schleife hänge.

Ich hoffe ihr könnt mir weiterhelfen.

Verwendet werden in meiner Umgebung:
- CubeMX zur Erzeugung des "Basiscodes" sowie der Chip Konfiguration
- CLion als IDE für die eigentliche Entwicklung
- cmake i.V.m. arm-none-eabi-gcc für die Kompilierung
- openocd für das flashen und debuggen mit einem ST-Link v2

Der Quellcode wurde vollständig durch cubeMX erstellt, somit gehe ich 
davon aus das die "üblichen" Probleme (bspw. eine falsche 
Implementierung von _sbrk ausgeschlossen sind, da dann ja alle davon 
betroffen wären). Habe diese trotzdem in der syscalls.c kontrolliert und 
sie entspricht den gängigen Varianten.

Weiterhin habe ich die Toolchain bereits gegen die vorkompilierte von 
arm ausgetauscht, jedoch ohne Änderung im Ergebnis.

Die linker Datei (STM32F103C8Tx_FLASH.ld) stammt ebenfalls original aus 
dem cubeMX. Auch der (häufig) vorgebrachte Vorschlag testweise heap und 
stack von 0x200/0x400 auf 0x1000 zu erhöhen bracht keine Änderung.

Folgend einmal sowohl die main.c (enthält den Testaufruf von snprintf) 
sowie die von CLion erzeugte CMakeList. Hierbei handelt es sich um die 
einzigen beiden Änderungen (Aufruf von snprintf() in main.c) und die von 
CLion erstellte CMakeList von dem von cubeMX erzeugten Basisdaten.

main.c
#include "main.h"
#include "spi.h"
#include "tim.h"
#include "gpio.h"

#include <stdio.h>

void SystemClock_Config(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_TIM1_Init();

  // Testaufruf von snprinft
  char buffer[64];
  snprintf(buffer, sizeof(buffer), "%d", 12); // <<-- HardFault

  while (1)
  {

  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{

}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{ 
}
#endif

CMakeList
SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)
cmake_minimum_required(VERSION 3.7)

SET(CMAKE_C_COMPILER_WORKS 1)
SET(CMAKE_C_COMPILER arm-none-eabi-gcc)
SET(CMAKE_CXX_COMPILER_WORKS 1)
SET(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER  arm-none-eabi-gcc)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(SIZE arm-none-eabi-size)

SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32F103C8Tx_FLASH.ld)
SET(COMMON_FLAGS
    "-mcpu=cortex-m3 ${FPU_FLAGS} -mthumb -mthumb-interwork -ffunction-sections -fdata-sections \
    -g -fno-common -fmessage-length=0 -specs=nosys.specs -specs=nano.specs")
SET(CMAKE_CXX_FLAGS_INIT "${COMMON_FLAGS} -std=c++11")
SET(CMAKE_C_FLAGS_INIT "${COMMON_FLAGS} -std=gnu99")
SET(CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,-gc-sections,--print-memory-usage -T ${LINKER_SCRIPT}")

PROJECT(Test C CXX ASM)
set(CMAKE_CXX_STANDARD 11)

add_definitions(-DUSE_HAL_DRIVER -DSTM32F103xB)

file(GLOB_RECURSE SOURCES "startup/*.*" "Drivers/*.*" "Src/*.*")

include_directories(Inc Drivers/STM32F1xx_HAL_Driver/Inc Drivers/STM32F1xx_HAL_Driver/Inc/Legacy Drivers/CMSIS/Device/ST/STM32F1xx/Include Drivers/CMSIS/Include)

add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})

set(CMAKE_EXE_LINKER_FLAGS
    "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map")

set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin)

add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
        COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:${PROJECT_NAME}.elf> ${HEX_FILE}
        COMMAND ${CMAKE_OBJCOPY} -Obinary $<TARGET_FILE:${PROJECT_NAME}.elf> ${BIN_FILE}
        COMMENT "Building ${HEX_FILE}
Building ${BIN_FILE}")

von Nop (Gast)


Bewertung
-5 lesenswert
nicht lesenswert
printf & co nutzt man auch nicht auf Microcontrollern. Andererseits, wer 
sich den Code von CubeMX erzeugen läßt, nutzt auch printf, wie man 
sieht.

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Gut, ich gebe offen zu, dass mir die einfache Verwendung von snprintf() 
zur Umwandlung einer int zu einem char[] einfach praktisch erschien und 
ja auch unterstützt wird (wenn sie auch nicht die 
platzsparenste/schnellste Methode darstellt). Da Geschwindigkeit/Platz 
bei meine Programm keine Rolle spielt (da ausreichend vorhanden).

Das ich mir die Chip-Konfiguration (Timer, GPIOs, usw.) von cubeMX 
erzeugen lasse scheint anscheinend auch ein Problem darzustellen. Hier 
sehe ich allerdings ohne begründete Argumente keinen Grund die 
Hardwarekonfiguration "per Fuß" durchzuführen, wenn der Hersteller 
dieser Hardware extra eine Software geschrieben hat, die das (ich setze 
mal fehlerfrei voraus) übernimmt.

von pegel (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Mit CubeIDE erzeugtes Projekt hat keine Probleme mit snprintf.

Siehe Debug Ausgabe direkt nach der Funktion im Anhang.

von Michael F. (michaelfu)


Bewertung
0 lesenswert
nicht lesenswert
Was sagen denn CFSR und HFSR über den Grund für den Hard Fault?

von pegel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kannst deine .ioc Projekt Datei mal anhängen.
Ich importiere die dann in CubeIDE.

Mal sehen, ob der Fehler vielleicht doch woanders liegt.

von Otto D. (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Anbei die .ioc Projekt Datei von cubeMX.

von pegel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Funktioniert ohne Probleme.
Das gleiche Ergebnis wie im Bild oben.

Muss an deiner IDE liegen.

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Michael F. schrieb:
> Was sagen denn CFSR und HFSR über den Grund für den Hard Fault?

Hat leider etwas gedauert, aber hier die beiden Register sobald er in 
den HardFault geht.
CFSR = 1024       (0b10000000000)
HFSR = 1073741824 (0b1000000000000000000000000000000)

von pegel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Was steht eigentlich in FPU_FLAGS?

Einen weiteren Unterschied sehe ich in -std=gnu99, ist bei mir 
-std=gnu11.

von samuel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bitte vergleiche doch mal deine CMakeLists.txt mit der von CubeMX 
generierten Makefile. Sind wirklich alle Flags identisch, insb. die für 
den Linker?

von samuel (Gast)


Bewertung
0 lesenswert
nicht lesenswert

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
samuel schrieb:
> Bitte vergleiche doch mal deine CMakeLists.txt mit der von CubeMX
> generierten Makefile. Sind wirklich alle Flags identisch, insb. die für
> den Linker?

Oh Mann, da sieht es ja aus wie Kraut und Rüben...
Anscheinend kann cLion zwar STM32, aber keine Makefiles lesen. Zumindest 
sehe ich da doch grössere Abweichungen.

CFlags die im Makefile vorhanden sind, aber in CMakeList fehlen:
-Og -Wall -gdwarf-2 -MMD -MP -MF""
Dafür hat cmake aber welche ergänzt:
-mthumb-interwork -fno-common -fmessage-length=0 -specs=nosys.specs -specs=nano.specs

Das selbe Spiel sehe ich bei den LDFlags:
Im Makefile vorhanden, aber fehlen in CMakeList:
-lc -lm -lnosys
Dafür in CMakeList vorhanden, aber nicht im Makefile:
-mthumb-interwork -ffunction-sections -fdata-sections -fno-common -fmessage-length=0 -specs=nosys.specs -g  --print-memory-usage  -Wl

Bin jetzt nicht der Experte was die C/LD Flags angeht, aber so eine 
große Abweichung sollte doch nicht vorkommen da cLion seine Vorgaben ja 
von cubeMX bekommt.

Anscheinend ist die "ach so ausgereifte" STM32 Unterstützung doch nicht 
so toll. Immerhin ist mein Anwendungsfall ein int in char umzuwandeln 
nun doch nicht so speziell.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
Das -specs=nosys.specs kommt mir verdächtig vor, kannst du das mal 
rausnehmen?

von pegel (Gast)


Bewertung
1 lesenswert
nicht lesenswert
€199.00 /Benutzer 1. Jahr

€159.00 /2. Jahr

€119.00 /ab dem 3. Jahr

Wau. Ist das fürs Hobby oder Zwang?
Nur für STM32?

von pegel (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> Das -specs=nosys.specs kommt mir verdächtig vor

CubeIDE setzt das auch ein.

von Johannes S. (jojos)


Bewertung
1 lesenswert
nicht lesenswert
https://www.embedded-software-engineering.de/amp/bare-bones-mit-gcc-und-c-a-674430/
Nosys ersetzt die Systemfunktionen durch leere stubs.

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
pegel schrieb:
> €199.00 /Benutzer 1. Jahr
>
> €159.00 /2. Jahr
>
> €119.00 /ab dem 3. Jahr
>
> Wau. Ist das fürs Hobby oder Zwang?
> Nur für STM32?

Hobby. Allerdings nicht nur für STM32. Die Einzellizenz für Nutzer 
(nicht Firmen) liegt bei €89 / Jahr. Und dann hat man die Version 
dauerhaft (auch wenn man nicht mehr zahl, man kriegt halt nur keine 
neueren Versionen mehr).

Hab damals mit python (pyCharm IDE) und Java (IntelliJ) angefangen 
(beides gratis Community Versionen vom selben Hersteller). Und in die 
IDE hab ich mich verguckt. Das Handling ist einfach bei allen gleich. 
Wollte deshalb für C/C++ auch eine IDE mit selben Funktionsumfang und 
Handling haben, dann fühlt man sich gleich zuhause.

Das die STM32 können war da nur "der Bonus".

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Johannes S. schrieb:
> 
https://www.embedded-software-engineering.de/amp/bare-bones-mit-gcc-und-c-a-674430/
> Nosys ersetzt die Systemfunktionen durch leere stubs.

Und wenn ich den Beitrag richtig verstehe, ist der Aufruf von nosys 
ungefährlich/überflüssig, da sie von nano sowieso erzwungen wird.

von pegel (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Also hätte ich jetzt das Problem, würde ich:
${FPU_FLAGS} ersetzen durch -mfloat-abi=soft
-std=gnu99 ersetzen durch -std=gnu11

und hinter -specs=nano.specs noch -lc -lm einfügen und probieren.

von Johannes S. (jojos)


Bewertung
0 lesenswert
nicht lesenswert
ich benutze Mbed, habe nachgesehen und da ist auch ein -lnosys drin.
Aber -specs=nano.specs gehört doch in die Linkerflags, und da fehlt das?

von Otto D. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
pegel schrieb:
> Also hätte ich jetzt das Problem, würde ich:
> ${FPU_FLAGS} ersetzen durch -mfloat-abi=soft
> -std=gnu99 ersetzen durch -std=gnu11
>
> und hinter -specs=nano.specs noch -lc -lm einfügen und probieren.

Danke! Das war die Lösung.
Habe "lc -lm" ergänzt und die FPU_FLAGS mfloat-abi=soft gesetzt (war 
leer).

Und schon ist er trotz nicht mehr gecrasht.
Schaut schonmal sehr gut aus, werde das morgen mal genauer testen und 
dann ein Update geben.

Ich möchte mich bei allen bedanken für die Hilfe.
Mir leuchtet nur nicht ein wieso die Flags falsch sind, da er sie ja von 
cubeMX ausliest. Dann müssten die ja bei ALLEN falsch sein (cubeMX ist 
der offizielle Weg in cLion die Quellen zu erzeugen).

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.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.