Ich hab grad ein interessantes Phänomen. Das anbei angehängte minimale Makefile-Projekt kompiliert einwandfrei und läuft auch einwandfrei auf einem STM32nucleo board SOFERN man im Makefile in den CFLAGS die Option -flto entfernt. Das interessante daran ist daß die Blinkzeit 500ms sich nicht ändert, jedoch die UART-Baudrate ist hinterher ungefähr doppelt so hoch, wenn man das Terminal auf dem PC auf 19200 einstellt kann man einige Zeichen des Strings "foobar\r\n" noch erahnen: 66 6F 6F E2 E1 72 8D 0A Ohne die Option kommt es wie gewünscht sauber auf 9600 Baud raus: 66 6F 6F 62 61 72 0D 0A Die Blinkzeit jedoch bleibt exakt gleich. Woran kann das liegen? Ist das ein Kompiler-Bug oder ein Bug in der HAL-Library oder eine fehlerhafte Anwendung meinerseits? ______ PS: Lässt sich dieses Minimalbeispiel eventuell noch schrumpfen (so um den Faktor 10 vielleicht)? Ich finde es schon einigermaßen ungeheuerlich wie ein so simples Minimalprogramm schon satte 4300 Byte konsumiert.
Mal ins Blaue geraten (deine Archive hab ich nicht durchwühlt): Optimierung nicht aktiviert? Oder nicht LTO-sicher programmiert?
Johann L. schrieb: > Optimierung nicht aktiviert? -Os denn Optimierung ist ja das Ziel der Übung. > Oder nicht LTO-sicher programmiert? Ich finde nichts im gcc manual zum Themembereich "LTO-sicher programmieren", es liest sich als sei es ausgereift und unproblematisch, hast Du evtl. mal ein paar weiterführende Links? Das einzige was mir bei nochmaligem Lesen noch aufgefallen ist war daß es sich nicht mit debug-Informationen verträgt, also hab ich die mal weggelassen, aber das hat keine Auswirkung.
"LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten verlässt (zB casts oder signed integer over/underflow etc.) darf der Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch aggressiver irgendwas... Technisch gesehen sind Funktionsnamen wie "__GPIOA_CLK_ENABLE" vom Standard schon verboten, daher ist es pures Glück dass diese vom Compiler überhaupt akzeptiert werden... Versuche mal das Problem zu lokalisieren: Lasse dir mal den Systemtakt auf dem Clock Output Pin ausgebem (MCO) und prüfe mit dem Oszilloskop ob der Takt mit und ohne LTO stimmt. Wenn er stimmt, ist der Fehler in der UART-Konfiguration, sonst in der Systemtakt-Konfiguration. Disassemblisiere mal das Kompilat, um zu schauen welche Werte in die Peripherie-Register geschrieben werden, und berechne ob sie stimmen (-> Reference Manual).
An sich klingt das nach einem angenehm einfachen Fall. Wenn die Bitrate der UART falsch ist, dann gibts nicht viele Stellen, an denen man suchen muss. Gibt weit schlimmere Fälle. Aber vollständiges Einhalten des Standards muss grad im µC Kontext nicht unbedingt ausreichen. Ich hatte mal den Fall, dass Optimierung einen kompletten Interrupt-Handler wegoptimierte, weil der nirgends im Programm referenziert wurde. In der Vektortabelle wurde er zwar schon angesprochen, aber der als "weak external" von der Standardumgebung definierte Default-Handler reichte dem Linker völlig aus. Je mehr Compiler/Linker vom Gesamtbild des Programms sehen, desto grösser ist die Chance, dass man auf solche Schmutzeffekte reinfällt. In meinem Fall ergab sich das als Folge von -ffunction-sections.
:
Bearbeitet durch User
Dr. Sommer schrieb: > "LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform > programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten > verlässt (zB casts oder signed integer over/underflow etc.) darf der > Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch > aggressiver irgendwas... Ja, genau das meinte ich mit "LTO-sicher". Die Probleme sind nicht compilerseitig, sondern es sind Fehler im Programm, die erst durch diese modulübergreifende Optimierung offensichtlich werden. Welche Kommandos werden denn konkret ausgeführt, d.h. wie wird gcc etc. aufgerufen? Nicht als make-Schnippel, sondern als Kommando, das von make ausgeführt wird.
:
Bearbeitet durch User
Johann L. schrieb: > Dr. Sommer schrieb: >> "LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform >> programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten >> verlässt (zB casts oder signed integer over/underflow etc.) darf der >> Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch >> aggressiver irgendwas... > > Ja, genau das meinte ich mit "LTO-sicher". Die Probleme sind nicht > compilerseitig, sondern es sind Fehler im Programm, die erst durch diese > modulübergreifende Optimierung offensichtlich werden. > > > Welche Kommandos werden denn konkret ausgeführt, d.h. wie wird gcc etc. > aufgerufen? Nicht als make-Schnippel, sondern als Kommando, das von > make ausgeführt wird. So sieht das aus wenn ich das gesamte Projekt neu baue.
1 | make clean all |
2 | rm -f *.o template.elf template.hex template.bin |
3 | arm-none-eabi-gcc -c -o stm32f4xx_hal.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c |
4 | arm-none-eabi-gcc -c -o stm32f4xx_hal_rcc.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c |
5 | arm-none-eabi-gcc -c -o stm32f4xx_hal_gpio.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c |
6 | arm-none-eabi-gcc -c -o stm32f4xx_hal_uart.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c |
7 | arm-none-eabi-gcc -c -o stm32f4xx_hal_dma.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c |
8 | arm-none-eabi-gcc -c -o stm32f4xx_hal_cortex.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c |
9 | arm-none-eabi-gcc -c -o stm32f4xx_hal_pcd.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c |
10 | arm-none-eabi-gcc -c -o stm32f4xx_hal_pcd_ex.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c |
11 | arm-none-eabi-gcc -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections -Wall -Wextra -Warray-bounds -Wno-unused-parameter -TDevice/gcc.ld src/main.c Device/startup_stm32f401xe.s src/stm32f4xx_hal_msp.c src/stm32f4xx_it.c src/system_stm32f4xx.c src/newlib_stubs.c stm32f4xx_hal.o stm32f4xx_hal_rcc.o stm32f4xx_hal_gpio.o stm32f4xx_hal_uart.o stm32f4xx_hal_dma.o stm32f4xx_hal_cortex.o stm32f4xx_hal_pcd.o stm32f4xx_hal_pcd_ex.o -o template.elf |
12 | arm-none-eabi-objcopy -O ihex template.elf template.hex |
13 | arm-none-eabi-objcopy -O binary template.elf template.bin |
14 | arm-none-eabi-size template.elf |
15 | text data bss dec hex filename |
16 | 2804 1104 36 3944 f68 template.elf |
Wie gesagt, es ist nur eine Art Hello-World. Als Grundlage hab ich das hier genommen: https://github.com/metabr/stm32-nucleo-f401re-basic-template hab das USB-zeug rausgeworfen und stattdessen den UART konfiguriert, indem ich aus anderen Turorials und Beispielprojekten zusammengesuchte Codeschnipsel passend gemacht habe. Ich muss dazu sagen daß ich eigentlich aus der AVR-Ecke komme, dort kann ich mittlerweile im Halbschlaf und mit verbundenen Augen ein neues Projekt aus dem Boden stampfen und es gibt erste Lebenszeichen von sich noch bevor die erste Tasse Kaffee leer ist. Bei ARM jedoch und im Speziellen bei dem STM32F401 den ich hier zum Spielen rumliegen habe steh ich noch ganz am Anfang, selbst die simpelsten Dinge erscheinen mir hier extremst verworren, der Aufbau der mitgelieferten Header und Libraries und vor allem deren Dokumentation liegen für mich noch weitgehend im Dunkeln (Ich suche immer noch den Anfang). Meine main.c sieht im wesentlichen so aus:
1 | int main(void) { |
2 | HAL_Init(); |
3 | SystemClock_Config(); |
4 | |
5 | /**
|
6 | * GPIO
|
7 | */
|
8 | |
9 | GPIO_InitTypeDef GPIO_InitStruct; |
10 | __GPIOA_CLK_ENABLE(); |
11 | GPIO_InitStruct.Pin = GPIO_PIN_5; |
12 | GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; |
13 | GPIO_InitStruct.Pull = GPIO_PULLUP; |
14 | GPIO_InitStruct.Speed = GPIO_SPEED_FAST; |
15 | HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); |
16 | |
17 | /**
|
18 | * UART
|
19 | */
|
20 | |
21 | GPIO_InitStruct.Pin = GPIO_PIN_2; |
22 | GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; |
23 | GPIO_InitStruct.Speed = GPIO_SPEED_FAST; |
24 | GPIO_InitStruct.Alternate = GPIO_AF7_USART2; |
25 | HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); |
26 | |
27 | __USART2_CLK_ENABLE(); |
28 | UART_HandleTypeDef UartHandle; |
29 | UartHandle.Instance = USART2; |
30 | UartHandle.Init.BaudRate = 9600; |
31 | UartHandle.Init.WordLength = UART_WORDLENGTH_8B; |
32 | UartHandle.Init.StopBits = UART_STOPBITS_2; |
33 | UartHandle.Init.Parity = UART_PARITY_NONE; |
34 | UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; |
35 | UartHandle.Init.Mode = UART_MODE_TX_RX; |
36 | HAL_UART_Init(&UartHandle); |
37 | |
38 | char buf[] = "foobar\r\n"; |
39 | |
40 | while (1) { |
41 | HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); |
42 | HAL_UART_Transmit(&UartHandle, (uint8_t *)buf, 8, 0xFFFF); |
43 | HAL_Delay(500); |
44 | }
|
45 | }
|
Und die SystemClock_Config(); sieht so aus (aber die hab ich direkt aus dem beispiel übernommen und dei scheint auch orginal aus dem Beispiel von ST für das Nucleo board zu stammen):
1 | /**
|
2 | * @brief System Clock Configuration
|
3 | * The system Clock is configured as follow :
|
4 | * System Clock source = PLL (HSI)
|
5 | * SYSCLK(Hz) = 84000000
|
6 | * HCLK(Hz) = 84000000
|
7 | * AHB Prescaler = 1
|
8 | * APB1 Prescaler = 2
|
9 | * APB2 Prescaler = 1
|
10 | * HSI Frequency(Hz) = 16000000
|
11 | * PLL_M = 16
|
12 | * PLL_N = 336
|
13 | * PLL_P = 4
|
14 | * PLL_Q = 7
|
15 | * VDD(V) = 3.3
|
16 | * Main regulator output voltage = Scale2 mode
|
17 | * Flash Latency(WS) = 2
|
18 | * @param None
|
19 | * @retval None
|
20 | */
|
21 | static void SystemClock_Config(void) { |
22 | RCC_ClkInitTypeDef RCC_ClkInitStruct; |
23 | RCC_OscInitTypeDef RCC_OscInitStruct; |
24 | HAL_StatusTypeDef status; |
25 | |
26 | /* Enable Power Control clock */
|
27 | __PWR_CLK_ENABLE(); |
28 | |
29 | /* The voltage scaling allows optimizing the power consumption when the device is
|
30 | clocked below the maximum system frequency, to update the voltage scaling value
|
31 | regarding system frequency refer to product datasheet. */
|
32 | __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); |
33 | |
34 | /* Enable HSI Oscillator and activate PLL with HSI as source */
|
35 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; |
36 | RCC_OscInitStruct.HSIState = RCC_HSI_ON; |
37 | RCC_OscInitStruct.HSICalibrationValue = 0x10; |
38 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; |
39 | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; |
40 | RCC_OscInitStruct.PLL.PLLM = 16; |
41 | RCC_OscInitStruct.PLL.PLLN = 336; |
42 | RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; |
43 | RCC_OscInitStruct.PLL.PLLQ = 7; |
44 | if((status = HAL_RCC_OscConfig(&RCC_OscInitStruct)) != HAL_OK) |
45 | {
|
46 | Error_Handler(); |
47 | }
|
48 | |
49 | /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
|
50 | clocks dividers */
|
51 | RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); |
52 | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; |
53 | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; |
54 | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; |
55 | RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; |
56 | if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) |
57 | {
|
58 | Error_Handler(); |
59 | }
|
60 | |
61 | /* Enable HSE Oscillator
|
62 | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
|
63 | RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
64 | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
|
65 | |
66 | if((status = HAL_RCC_OscConfig(&RCC_OscInitStruct)) != HAL_OK)
|
67 | {
|
68 | Error_Handler();
|
69 | }
|
70 | */
|
71 | }
|
Ich glaube nicht daß der Takt doppelt so schnell läuft, das scheint bereits im Normalzustand (also wenn der Fehler nicht auftritt) den höchstmöglichen Takt einzustellen.
Lad dir mal das STM CUBE tool und die Lib für deinen F4. Damit kannst du dir erstmal alles zusammen klicken. Später nimmst du nur die Sachen die du wirklich brauchst. Optimierung -Og maximale Optimierung mit brauchbaren Debug Infos. Gruss, Bernd
Im Wiki steht, dass es zB möglich ist, dass Interruptvector und ISRs möglicherweise wegoptimiert werden. Dagegen soll __attribute(used) oder so ähnlich helfen. Hat es bei mir aber auch nicht.
Ok, Leute, ich glaub ich hab die Ursache. War natürlich mein eigener Fehler (wie immer), hab nochmal in Ruhe über das drübergeschaut was ich gestern fabriziert habe und siehe da:
1 | UART_HandleTypeDef UartHandle; |
sollte ich wohl mal mit 0 initialisieren, besser ist das...
1 | UART_HandleTypeDef UartHandle = {0}; |
Und o Wunder: nun stimmt die Baudrate :-)
Deswegen gibt's da eine init struct Funktion.. Die erstmal alles auf default setzt. Ich hatte vor 2jahren erste mL Kontakt zum cortex. Mittlerweile hab ich die stm/nxp/.. libs über.. Ich bau mir eigene Funktionen .. Und oft spreche ich Register direkt an. Das ganze gewabel mit den structs und so is mir zu nervig. Die libs nutze ich nur als grobe Vorlage wenn ich mal wissen will was ich brauch.. oder kopiere mir den Teil da raus... Seit dem sind die cortex M genau so einfach wie die AVR's ^^
huhu schrieb: > Und oft spreche ich Register direkt an. Du traust dich ja was. Und wie ist die Reaktion, kommt ihr ins Gespräch? Wat ein Gebrabbel. Es ist ganz einfach: Je proffessioneler desto abstrakter. Der Code soll wiederverwendbar sein, denn er kostet eine Menge Geld. Ob fertige LIBS vom Hersteller, vom Tool, vom ..., oder auch eigene. Aber LIBS und abstrakt!
Das gemeine ist daß ich diese struct initialisierung 1:1 vom mitgelieferten Beispiel aus dem STM32Cube übernommen habe: http://pastebin.com/f3HyRgG3 huhu schrieb: > Deswegen gibt's da eine init struct Funktion.. > Die erstmal alles auf default setzt. Wo find ich die?
Ja.... Abstrakt ist schon richtig. Die meisten AVR umsteiger stolpern aber über die libs der Hersteller.. Beim avr haben die meisten ins Datenblatt geschaut Und da werden Register direkt per bittschieberei beschrieben Wenn man das anfangs mal weglässt merkt man das es das gleiche ist... Das struct zeug ist nur ein optisch komplizierterer Einstieg. Später wird man es Evtl genau so machen... Aber deswegen ist der cortex M nicht komplizierter
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.