Forum: Mikrocontroller und Digitale Elektronik STM32 + UART + volatile + IRQ -O0 = Probleme!


von X. A. (wilhem)


Lesenswert?

Hallo Leute,
ich besitze ein Discovery Board mit einem STM32F417VG bestückt. Mit dem 
Board versuche ich nun die serielle Schnittstelle mit Interrupts in 
Betrieb zu nehmen.
Kompilierungsumgebung läuft unter Linux (Xubuntu) und die kompilierten 
Datei werden über st-link zu dem Board übertragen.

Mein Problem? Ich habe den folgenden Forum 
(https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32Discovery/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2FSTM32Discovery%2FUART%20example%20code%20for%20STM32F0&FolderCTID=0x01200200770978C69A1141439FE559EB459D75800084C20D8867EAD444A5987D47BE638E0F&currentviews=41266) 
durchgelesen und habe ein paar Beispiele an mein Programm angepasst.
Ich habe aber nun das Problem, dass mein Programm nicht das gewünschte 
Resultat liefert. Hier ist ein minimales Beispiel:
1
#include <stdio.h>
2
#include <string.h>
3
4
#define USE_STDPERIPH_DRIVER
5
6
#include "stm32f4xx.h"
7
8
#define MAX_SIZE_BUFFER 254
9
10
volatile char rx_data;
11
12
void SystemConfiguration( void );;
13
14
15
int main( int argc, char *argv[] )
16
{
17
  /* Konfiguration des Systems */
18
  SystemConfiguration();
19
20
  /* Initialisierung des Busses PORTA & PORTD */
21
  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA, ENABLE );
22
  RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD, ENABLE );
23
24
  /* Initialisierung für die USART2 */
25
  RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
26
27
28
  /* Initialisierung des PORTD PD15 als Output */
29
  GPIO_InitTypeDef GPIO_PORTD_Struc;
30
  GPIO_StructInit( &GPIO_PORTD_Struc );
31
  GPIO_PORTD_Struc.GPIO_Pin   = GPIO_Pin_15;
32
  GPIO_PORTD_Struc.GPIO_Mode  = GPIO_Mode_OUT;
33
  GPIO_PORTD_Struc.GPIO_Speed = GPIO_Medium_Speed;
34
  GPIO_PORTD_Struc.GPIO_OType = GPIO_OType_PP;
35
  GPIO_PORTD_Struc.GPIO_PuPd  = GPIO_PuPd_NOPULL;
36
37
  /* Struktur initialisieren */
38
  GPIO_Init( GPIOD, &GPIO_PORTD_Struc );
39
40
41
  GPIO_PinAFConfig( GPIOA, GPIO_PinSource2, GPIO_AF_USART2 );
42
  GPIO_PinAFConfig( GPIOA, GPIO_PinSource3, GPIO_AF_USART2 );
43
44
  /* Initialisierung des PORTA PA2 und PA3 für die USART2 */
45
  GPIO_InitTypeDef GPIO_PORTA_Struc;
46
  GPIO_StructInit( &GPIO_PORTA_Struc );
47
  GPIO_PORTA_Struc.GPIO_Pin   = ( GPIO_Pin_2 | GPIO_Pin_3 );
48
  GPIO_PORTA_Struc.GPIO_Mode  = GPIO_Mode_AF;
49
  GPIO_PORTA_Struc.GPIO_Speed = GPIO_Fast_Speed;
50
  GPIO_PORTA_Struc.GPIO_OType = GPIO_OType_PP;
51
  GPIO_PORTA_Struc.GPIO_PuPd  = GPIO_PuPd_UP;
52
53
  /* Struktur initialisieren */
54
  GPIO_Init( GPIOA, &GPIO_PORTA_Struc );
55
56
  /* Struktur für den Interrupt */
57
  NVIC_InitTypeDef NVIC_Struc;
58
  NVIC_Struc.NVIC_IRQChannel                   = USART2_IRQn;
59
  NVIC_Struc.NVIC_IRQChannelCmd                = ENABLE;
60
  NVIC_Struc.NVIC_IRQChannelPreemptionPriority = 0x0F;
61
  NVIC_Struc.NVIC_IRQChannelSubPriority        = 0x0F;
62
  NVIC_Init( &NVIC_Struc );
63
64
65
  /* Definition der USART */
66
  USART_ClockInitTypeDef USART_Clock_Struc;
67
  USART_ClockStructInit( &USART_Clock_Struc );
68
  USART_ClockInit( USART2, &USART_Clock_Struc );
69
70
  USART_InitTypeDef USART_Struc;
71
  USART_StructInit( &USART_Struc );
72
  USART_Struc.USART_BaudRate            = 115200;
73
  USART_Struc.USART_WordLength          = USART_WordLength_8b;
74
  USART_Struc.USART_StopBits            = USART_StopBits_1;
75
  USART_Struc.USART_Parity              = USART_Parity_No;
76
  USART_Struc.USART_Mode                = ( USART_Mode_Rx | USART_Mode_Tx );
77
  USART_Struc.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
78
79
  /* USART2 Initialisieren */
80
  USART_Init( USART2, &USART_Struc );
81
82
  USART_Cmd( USART2, ENABLE );
83
84
  USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );
85
86
87
  /* Endloser Loop */
88
  while(1) {
89
90
    if( rx_data == 'X' ) {
91
92
      /* Schalte die blaue LED an */
93
      GPIO_SetBits( GPIOD, GPIO_Pin_15 );
94
    }
95
  };
96
97
  return 0;
98
}
99
100
101
void SystemConfiguration( void )
102
{
103
  /* Initialisierung der Kernfunktionen */
104
  SystemInit();
105
106
  /* Initialisierung des Timers */
107
  SysTick_Config( SystemCoreClock / 1000 );
108
}
109
110
void USART2_IRQHandler( void )
111
{
112
113
  if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET ) {
114
115
    rx_data = USART_ReceiveData( USART2 );
116
  }
117
118
  USART_ClearITPendingBit( USART2, USART_IT_RXNE );
119
}

Im Grunde initialisiere ich das Kernsystem, den Clock und den 
PORTA/PORTD, um die Led LD15 auf der Platine leuchten zu lassen. Ich 
sende ein Byte über mein PC zu dem Board, ein Interrupt wird ausgelöst 
und der Byte wird zu einer als volatile bezeichneten Variable 
zugewiesen (rx_data).
Die LED leucht aber gar nicht...

Ihr fragt nun bestimmt: "Na klar..ist die serielle Verbindung nicht 
i.O".
Doch wenn ich die USART2_IRQHandler funktion wie folgend umschreibe:
1
void USART2_IRQHandler( void )
2
{
3
4
  if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET ) {
5
6
    rx_data = USART_ReceiveData( USART2 );
7
             
8
                /* Aus dem main-Loop entfernt und hier heruntergebracht */
9
    if( rx_data == 'X' ) {
10
      GPIO_SetBits( GPIOD, GPIO_Pin_15 );
11
    }
12
  }
13
14
  USART_ClearITPendingBit( USART2, USART_IT_RXNE );
15
}

dann geht doch. Nach Übertragung der gewünschten Buchstabe, leuchtet die 
LED wie erwartet.

So dachte ich mir..."es sieht so aus, als ob die while-Schleife in dem 
Loop wegoptimiert worden ist..."
Hier die Flags, die ich in meinem Makefile eingestellt habe:
1
CC=arm-none-eabi-gcc
2
OBJCOPY=arm-none-eabi-objcopy
3
4
CFLAGS  = -g -O0 -Wall -Tstm32_flash.ld
5
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
6
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -D"assert_param(expr)=((void)0)"
7
CFLAGS += -I.
8
9
# Include files from STM libraries
10
CFLAGS += -I$(STM_COMMON)/Libraries/STM32F4xx_StdPeriph_Driver/inc
11
CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Include 
12
CFLAGS += -I$(STM_COMMON)/Libraries/CMSIS/Device/ST/STM32F4xx/Include

Egal welche Optimierungsstufe eingebe - -O[s0123] - kriege ich immer 
dasselbe zurück. Die Led leuchtet gar nicht.

Wo ist der Fehler?

Wie gesagt, das ist ein minimal Beispiel. Das echte Programm ist etwa 
komplexer. Ich möchte aber herausfinden, wo das Problem auftrifft und 
warum.

Ich danke Euch für Eure Hilfe.

Gruß

von Eiermann (Gast)


Lesenswert?

Salam,
guck dir nochmals die Reihenfolge der Initialierungen an.

Das hier:
> GPIO_PinAFConfig( GPIOA, GPIO_PinSource2, GPIO_AF_USART2 );
> GPIO_PinAFConfig( GPIOA, GPIO_PinSource3, GPIO_AF_USART2 );

> USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );
kommt mir Spanisch vor. Kann aber auch richtig sein, da du die neuere 
lib hast.

Jedenfalls, Interrupts wie
> USART_ClearITPendingBit( USART2, USART_IT_RXNE );
müssen gleich anfangs einer ISR gelöscht werden.

mfg

von X. A. (wilhem)


Lesenswert?

Eiermann schrieb:

> Das hier:
>> GPIO_PinAFConfig( GPIOA, GPIO_PinSource2, GPIO_AF_USART2 );
>> GPIO_PinAFConfig( GPIOA, GPIO_PinSource3, GPIO_AF_USART2 );
>
>> USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );
> kommt mir Spanisch vor. Kann aber auch richtig sein, da du die neuere
> lib hast.

Sorry...
und was ist verkehrt in der obigen Reihenfolge?
Zu sagen "schau mal..." hilft mir nicht wirklich weiter.
Wie soll es sonst sein?

Falls es dir Spanisch vorkommt, das schaue mal in den von mir geposteten 
Link oben. In den Beispiele ist die Reihenfolge genauso, wie ich 
gepostet habe.

> Jedenfalls, Interrupts wie
>> USART_ClearITPendingBit( USART2, USART_IT_RXNE );
> müssen gleich anfangs einer ISR gelöscht werden.

Sicher? Und was ist wenn der Mikro ein zweites Zeichen nach dem ersten 
empfängt?
Außerdem habe ich festfestellt, dass auch ohne den Interrupt zu löschen, 
wird die IRQHandler() wieder aufgerufen.

Das Problem ist, wie gesagt, dass der hautmain Loop einfach ignoriert 
wird. Obwohl ich die Variable als volatile deklariert habe.

: Bearbeitet durch User
von Cube_S (Gast)


Lesenswert?

Da ich auch gerade am Spielen mit einem Discovery-Board (STM32F407) bin 
habe ich mir das mal 1:1 reinkopiert. In meiner Entwicklungsumgebung 
(VisualGDB) entfällt das
1
SystemConfiguration()
 und der Interrput wird so initialisiert:
1
NVIC_EnableIRQ(USART2_IRQn);
Der Rest ist unverändert. Die Variante mit der volatile-Variablen 
funktioniert bei mir sowohl in der Debug als auch in der 
Release-Version. Ich kann da auch keinen Fehler erkennen.

>Sicher? Und was ist wenn der Mikro ein zweites Zeichen nach dem ersten
>empfängt?
Sollte man nicht genau aus diesem Grund das Flag so schnell wie möglich 
zurücksetzen (also möglichst am Anfang der ISR)?

von W.S. (Gast)


Lesenswert?

Dave A. schrieb:
> Ich habe aber nun das Problem, dass mein Programm nicht das gewünschte
> Resultat liefert.

Nein, du hast ganz andere Probleme.

Zuvörderst, daß du dich mit Linux betust, weswegen du auch Linkerfiles, 
Makefiles und dergleichen benutzt, was in vielen Fällen Fehlerurschen 
beherbergt und wozu ich als Windows-Benutzer dir keinen Ratschlag geben 
kann.

Sodann hast du das Problem, daß du völlig unstrukturiert 
drauflosschreibst, so daß alles bunt durcheinanderpurzelt und selbst 
sowas wie Details von Treibersetup's in main vorkommen. Obendrein lese 
ich sowas wie "GPIO_StructInit".

Such einfach mal in diesem Forum, ich hatte vor einiger Zeit schon mal 
nen Unit für die U(S)ART's beim STM32F302 gepostet, an anderen Stellen 
findet sich auch ne Menge an Lehrreichem, was die vernünftige 
Strukturierung und diverse Peripherie-Treiber nebst deren Schnittstellen 
zur restlichen Firmware betrifft. Also stell dich nicht so an.

Ich würde an deiner Stelle zu allererst und in folgender Reihenfolge 
tun:
1. nen Startupcode für deinen µC in Assembler 
schreiben/beschaffen/kopieren und lesen und verstehen.
2. ne separate Quelle zum allgemeinen Systemsetup schreiben, wo die 
Pinverwendung in sinnvoller Weise erledigt wird, dann der Takt 
aufgesetzt wird und ggf. sowas wie externern SDRAM oder so aufgesetzt 
wird.
3. ne Quelle für eine Systemuhr auf SysTick-Basis schreiben
4. ein main schreiben, was aber bittesehr NICHT mit 
Peripherie-Firlefanz zugepflastert wird
5. dieses Minimalzeugs erstmal mit wackelndem Pin am Oszi oder so zum 
Laufen bringen
6. dann Peripherietreiber schreiben für UART's und ggf. VCP via USB. 
(Quelle dazu gibt's hier auch, such einfach danach)

W.S.

von W.S. (Gast)


Lesenswert?

Cube_S schrieb:
> Sollte man nicht genau aus diesem Grund das Flag so schnell wie möglich
> zurücksetzen (also möglichst am Anfang der ISR)?

Du hast offensichtlich weder das Manual wirklich gründlich gelesen, noch 
den E/A Verkehr per Interrupt selbst ausprobiert.

Also: Es gibt je nach Chip und peripherem Core unterschiedliche 
Verhaltensweisen der HW bezüglich Interrupts: manche erzeugen z.B. nen 
TxEmpty-Interrupt nur beim Leer-WERDEN, andere beim Leer-SEIN. 
Entsprechend muß das Verhalten des Handlers sein: entweder garnix tun 
oder bei nachfolgendem Nichtbedarf des Interrupts den Interrupt im 
Peripheriecore abschalten.

Viele Flags kann man nämlich überhaupt nicht zurücksetzen. Die setzen 
sich nur dann zurück, wenn man z.B. den Sendepuffer mit dem nächsten 
Byte füllt. Hat man aber kein nächstes Byte, dann wird man das TxEmpty 
Flag niemals mehr los bis zur nächste Ausgabe zum UART. Folglich muß 
man, um nicht im Dauer-Interrupt zu ersticken, das TXEIE ausschalten und 
mit dem nächsten Schreiben in den internen Ringpuffer eben wieder 
einschalten.

Klaro?

W.S.

von Cube_S (Gast)


Lesenswert?

> Klaro?

Und wie. Ich habe schon ein paar funktionierende Interrupt-Routinen am 
laufen, da mach Dir mal keine Sorgen.

Je länger man aber damit wartet ein Interrupt-Flag zu löschen (ob nun 
automatisch wie hier oder nicht) desto größer wird die Chance, dass ein 
Neusetzen des Flags durch die Hardware aufgrund eines verspäteten 
Löschens durch die Software verpasst wird.

Klaro?

von X. A. (wilhem)


Lesenswert?

Cube_S schrieb:
> Da ich auch gerade am Spielen mit einem Discovery-Board (STM32F407) bin
> habe ich mir das mal 1:1 reinkopiert. In meiner Entwicklungsumgebung
> (VisualGDB) entfällt das
>
1
SystemConfiguration()
 und der Interrput wird so initialisiert:
>
1
> NVIC_EnableIRQ(USART2_IRQn);
2
>
> Der Rest ist unverändert. Die Variante mit der volatile-Variablen
> funktioniert bei mir sowohl in der Debug als auch in der
> Release-Version. Ich kann da auch keinen Fehler erkennen.
>
>>Sicher? Und was ist wenn der Mikro ein zweites Zeichen nach dem ersten
>>empfängt?
> Sollte man nicht genau aus diesem Grund das Flag so schnell wie möglich
> zurücksetzen (also möglichst am Anfang der ISR)?

Danke Cube_S!!
Kannst Du mir bitte sagen, welche Optimierungsstufen bzw. Optionen dem 
Compiler eingegeben hast?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

W.S. schrieb:
> Obendrein lese
> ich sowas wie "GPIO_StructInit".

Dagegen ist nichts zu sagen, damit wird eine grundlegende Vorbelegung 
der Struct initialisiert - alles bestens.

von X. A. (wilhem)


Lesenswert?

W.S. schrieb:
> Nein, du hast ganz andere Probleme.
>
> ...

Ehy, deine Arroganz und deine Klugscheißerei passen hier nicht so ganz.
Vor allem, weil du nur gelabert hast, ohne die Kernfrage - scheinbar - 
gelesen und verstanden zu haben. Und zwar, warum das Programm in die 
Interrupt-Schleife geht und nicht in die MainRoutine.
Cube_S hat netterweise das Programm auf seinem Board getestet und somit 
mir eine wichtige Info gegeben. Ein Forum oder eine Community - wie 
diese - zeichnet sich durch Hilfsbereitschaft und Ideeaustausch und noch 
mehr aus. Daher...wenn du dich wegen den Libs von STM angekotzt fühlst, 
dann vermeide diesen Thread. Keiner hat dich eingeladen. Und keiner will 
dass Diskussionen jedesmal eskalieren.
Wenn du irgendwas interessant in der Vergangenheit gepostet hast, kannst 
du hier posten. Ohne zu nörgeln.

: Bearbeitet durch User
von Cube_S (Gast)


Lesenswert?

Zu den Einstellungen: In der Entwicklungsumgebung einstellbar und 
sichtbar sind bei mir
1
Debug: -ggdb -ffunction-sections -O0
2
Release: -ggdb -ffunction-sections -O3

In der Ausgabe dann entsprechend:
1
1>  C:\SysGCC\arm-eabi/bin/arm-eabi-gcc.exe -ggdb -ffunction-sections -O0   -mcpu=cortex-m4 -mthumb -mfloat-abi=soft -I. -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/STM32F4xx_StdPeriph_Driver/inc -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/Device/ST/STM32F4xx/Include -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/Include -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/RTOS/Template -DDEBUG=1 -DHSE_VALUE=8000000 -DARM_MATH_CM4 -DSTM32F407VG -Dstm32_flash_layout -DSTM32F40_41xxx  -c main.c -o Debug/main.o -MD -MF Debug/main.dep
2
1>  C:\SysGCC\arm-eabi/bin/arm-eabi-gcc.exe -ggdb -ffunction-sections -O3   -mcpu=cortex-m4 -mthumb -mfloat-abi=soft -I. -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/STM32F4xx_StdPeriph_Driver/inc -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/Device/ST/STM32F4xx/Include -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/Include -IC:\Prj\Stm32\VisualGDB\Shared/STM32F4xxxx/CMSIS_StdPeriph/RTOS/Template -DNDEBUG=1 -DRELEASE=1 -DHSE_VALUE=8000000 -DARM_MATH_CM4 -DSTM32F407VG -Dstm32_flash_layout -DSTM32F40_41xxx  -c main.c -o Release/main.o -MD -MF Release/main.dep

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Im alten Atollic hatte ich merkwürdige Effekte, wenn ich in einer 
(Timer) ISR Unterprogramme aufrief, die aber auch nur aus der ISR 
aufgerufen wurden.
Sie wurden einfach nicht ausgeführt und der MC sprang in den Hard-Fault 
Handler. Das triffts bei dir nicht ganz, spricht aber für manchmal 
eigenartigen ISR Benimm.

Ich habe das Programm damals abgeändert, den Code aber dann nie mehr 
durch mein derzeitiges Coocox gejagt, weil das Projekt auf Eis gelegt 
wurde.

Die Frage ist nun, ist DMA nicht sinnvoller als eine ISR und was 
möchtest du eigentlich mit dem empfangenen Zeichen anfangen? Als erstes 
würde ich mal das IRQ Flag direkt am Anfang löschen und dann eine 
sinnvolle Behandlung des Zeichens einfügen, z.B. Schreiben in einen 
Buffer oder so. Evtl. löst sich das Problem von alleine.
Ach, du kannst übrigens beim F417 gerne auch hard math einschalten, der 
MC hat eine FPU, die sich sonst langweilt. Aber das hat mit dem Problem 
nix zu tun.

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